# --*--coding:utf-8--*--

import traceback
from urllib.parse import unquote
import pyperclip
import string
import copy
import ssl
import threading
import webbrowser
from concurrent.futures import ThreadPoolExecutor
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
from binascii import a2b_hex
import json
import queue
import requests
import random
import re
import datetime
import os
import configparser
import PySimpleGUI as sg
import time
from urllib3 import encode_multipart_formdata

# 忽略未经核实的SSL证书认证警告。解决https请求经过fiddler调用时会报错
ssl._create_default_https_context = ssl._create_unverified_context
requests.packages.urllib3.disable_warnings()

t_windows = None
q = queue.Queue()  # 线程普通队列 # 记录多线程状态提单成功的单号boeNo和taskId
boe_jump_ths_queue = queue.Queue()  # 线程普通队列  # 记录多线程状态财务审批跳转共享成功的单号
timeout = 30
lock = threading.Lock()  # 线程锁

boeNo = None
list_boeNo = []  # 记录全部生成的单据，全部的意思是自工具启动开始
fial_boeNo = []  # 记录全部失败的单据
success_boeNo = []  # 记录全部成功共享审批的单据
success_boe_pay = []  # 记录全部成功共享审批的单据+支付指令
list_boeNo_now = []  # 记录'本次执行'生成的单据列表
fial_boeNo_now = []  # 记录'本次执行'失败的单据列表
Number_Submit = 0  # 提单次数，用于进度
MaxWorkers = 10  # 最大线程数
submit_num_ths = 0
jump_num_ths = 0  # 记录跳转执行次数
jump_success_ths = 0  # 记录跳转成功执行次数
num_there_ths = 0  # 记录执行‘完成’次数
# 右键菜单
right_button_menu = ['刷新', '清空', '源码']
# 支持的场景列表
list_bill_type = ['1_批量提单', '2_数据工厂', '3_请求体格式化']
list_test_env = ['https://fol.ztccloud.com.cn', 'http://192.168.2.53', 'http://192.168.2.74']
# 提单账号列表
list_user_submit = ['0901', '0914', 'XY01', 'apiAuto1']
# 数据终点
destination_list = ["仅提单", "完成业务审批", "完成共享审批"]
# 基础数据业务场景
list_base = ['公司部门', '客商管理', '临时客商', '项目管理', '维度管理',
             '科目体系', '汇率方案', '账套管理', '税种税率', '公司账户',
             '结算方式', '业务类型', '会计结构', '凭证配置', '特殊总账',
             '记账代码']

# '会计结构', '凭证配置', '特殊总账'
list_base_more = ['业务科目配置', '税务科目配置', '结算科目配置', '银行/票据科目配置', '会计结构建立',
                  '辅助核算建立', '特殊总账类型', '特殊总账标识', '客商科目分配']

list_base_key = ['data_comp_dept', 'data_vendors', 'data_temp_vendors', 'data_project', 'data_flexvaluesets',
                 'data_coaaccountscheme', 'data_exchangerate', 'data_sob', 'data_taxrate', 'data_companybank',
                 'data_paymentmode', 'data_category', 'data_acctsubconf', 'data_acctstructures', 'data_specialgl',
                 'data_postingkey']

list_base_num = [0] * len(list_base)

list_base_num_more = [0] * len(list_base_more)
# 基础配置业务场景
list_baseconfig = ['项目&核算主体', '账套&汇率&科目']
ticketDefineTemplateId = ""

tool_list = ['bill_type', 'times', 'destination', 'user_submit', 'list_user_submit', 'passwd_submit', 'boe_body', 'list_test_env', 'nodename']  # 界面字段、顺序不敏感
config_list = []  # 非界面字段

# 数据工厂UI
def tool_data_ui(data, **kwargs):
    global list_test_env
    try:
        if len(eval(data['list_test_env'])):
            list_test_env = eval(data['list_test_env'])
    except:
        sg.popup("配置文件中'list_test_env'必须为list")
        return
    layout_title = [
        [
            sg.Text('* 测试环境：', size=15, justification='right'),
            sg.InputCombo(key='base_url', size=20, default_value=test_env, values=list_test_env,
                          enable_events=True),
            sg.Text('* 场景：', size=15, justification='right'),
            sg.InputCombo(key='bill_type', size=20, default_value=list_bill_type[1],
                      values=list_bill_type, readonly=True, enable_events=True),
            sg.Text('末尾特征标记：', size=15, justification='right'),
            sg.Input(key='namedflag', size=22, default_text="autoTest_", tooltip="最长10位"),
        ],
    ]
    layout_basedata = [
        [
            sg.Checkbox(f'       {list_base[0]}：', key=f"{list_base_key[0]}", size=11, default=False),
            # sg.Text('* 父节点Name/Code：', size=10, justification='right'),
            sg.Input(key='root', size=22, tooltip="父节点Name/Code\n名字/编码要一起填写", default_text=""),
            sg.Input(key='comp_num', size=19, tooltip="公司数量", default_text="1"),
            sg.Input(key='dept_num', size=19, tooltip="公司下部门数量", default_text="1"),
            sg.Input(key='post_num', size=20, tooltip="部门下岗位数量", default_text="1"),
            sg.Input(key='emp_num', size=20, tooltip="岗位下员工数量", default_text="1"),
        ],
        [
            sg.Checkbox(f'       {list_base[1]}：', key=f"{list_base_key[1]}", size=11, default=False),
            sg.Input(key='num_vendors', size=10, tooltip="客商数量", default_text="1"),
            sg.Input(key='num_vendors_comp', size=10, tooltip="每个客商的科目分配数量\nSAP相关配置[客商科目分配]", default_text="1"),
            sg.Checkbox(f'         {list_base[2]}：', key=f"{list_base_key[2]}", size=12, default=False),
            sg.Input(key='num_temp_vendors', size=22, tooltip="临时供应商数量", default_text="1"),
            sg.Checkbox(f'         {list_base[3]}：', key=f"{list_base_key[3]}", size=12, default=False),
            sg.Input(key='num_project', size=22, tooltip="项目数量", default_text="1"),
        ],
        [  # flexvaluesets
            sg.Checkbox(f'       {list_base[4]}：', key=f"{list_base_key[4]}", size=11, default=False),
            sg.Input(key='num_flexvaluesets', size=10, tooltip="维度数量", default_text="1"),
            sg.Input(key='num_flexvaluesetscustom', size=10, tooltip="维度下成员的数量", default_text="1"),
            sg.Checkbox(f'         {list_base[5]}：', key=f"{list_base_key[5]}", size=12, default=False),
            sg.Input(key='num_coaaccountscheme', size=10, tooltip="科目体系数量", default_text="1"),
            sg.Input(key='num_coaaccounterp', size=10, tooltip="科目体系下会计科目数量", default_text="1"),
            sg.Checkbox(f'         {list_base[6]}：', key=f"{list_base_key[6]}", size=12, default=False),
            sg.Input(key='num_exchangerate', size=10, tooltip="汇率方案数量", default_text="1"),
            sg.Input(key='num_exchangerate_conf', size=10, tooltip="汇率方案下汇率的数量", default_text="1"),
        ],
        [
            sg.Checkbox(f'       {list_base[7]}：', key=f"{list_base_key[7]}", size=11, default=False),
            sg.Input(key='num_sob', size=7, tooltip="账套数量", default_text="1"),
            sg.Input(key='num_fule', size=6, tooltip="账套下核算主体数量", default_text="2"),
            sg.Input(key='num_fulecc', size=5, tooltip="账套下核算主体下成本中心数量", default_text="5"),
            sg.Checkbox(f'         {list_base[8]}：', key=f"{list_base_key[8]}", size=12, default=False),
            sg.Input(key='num_taxrate', size=22, tooltip="税种税率", default_text="1"),
            sg.Checkbox(f'         {list_base[9]}：', key=f"{list_base_key[9]}", size=12, default=False),
            sg.Input(key='num_companybank', size=10, tooltip="公司账户数量", default_text="1"),
            sg.Input(key='num_comp2bank', size=10, tooltip="【公司账户】配置到【会计结构菜单】-【银行/票据科目配置】的数量", default_text="1"),
        ],
        [
            sg.Checkbox(f'       {list_base[10]}：', key=f"{list_base_key[10]}", size=11, default=False),
            sg.Input(key='num_paymentmode', size=22, tooltip="结算方式", default_text="1"),
            sg.Checkbox(f'         {list_base[11]}：', key=f"{list_base_key[11]}", size=12, default=False),
            sg.Input(key='num_category', size=10, tooltip="业务大类数量", default_text="1"),
            sg.Input(key='num_small_category', size=10, tooltip="每个业务大类下小类数量", default_text="1"),
            sg.Checkbox(f'         {list_base[12]}：', key=f"{list_base_key[12]}", size=12, default=False),
            sg.Input(key='num_acctsubconf', size=22, tooltip="会计结构数量，分别是：\n'业务科目配置', '税务科目配置', '结算科目配置'\n"
                                   "执行前最好先把依赖的业务类型、税种税率、结算方式创建完成", default_text="1"),
        ],
        [
            sg.Checkbox(f'       {list_base[13]}：', key=f"{list_base_key[13]}", size=11, default=False),
            sg.Input(key='num_acctstructures', size=22, tooltip="凭证配置[会计结构建立]、[辅助核算建立]", default_text="1"),
            sg.Checkbox(f'         {list_base[14]}：', key=f"{list_base_key[14]}", size=12, default=False),
            sg.Input(key='num_specialgl', size=22, tooltip="SAP相关配置[特殊总账类型]、[特殊总账标识]", default_text="1"),
            sg.Checkbox(f'         {list_base[15]}：', key=f"{list_base_key[15]}", size=12, default=False),
            sg.Input(key='num_postingkey', size=22, tooltip="SAP相关配置[记账代码]", default_text="1"),
        ],

        [
            sg.Button(
                "全√",
                key='allin',
                size=10,
                mouseover_colors='#dec674',
                use_ttk_buttons=True,
                tooltip='点击打开配置文件进行配置,右键获取工具源码',
                right_click_menu=['&Right',
                                  right_button_menu]),
            sg.Text('  ', size=77, justification='left'),  # 空白区域
            sg.Button(
                "執行",
                bind_return_key=True,
                size=10,
                mouseover_colors='#1d953f',
                use_ttk_buttons=True),
            sg.Button(
                "進度",
                bind_return_key=True,
                size=10,
                mouseover_colors='#1d953f',
                use_ttk_buttons=True),
        ]
    ]
    layout_baseconfig = [
        [
            sg.Text('* 场景：', size=14, justification='right'),
            sg.InputCombo(key='baseconfig', size=20, values=list_baseconfig, readonly=True, enable_events=True),
            sg.Text('* 数量级：', size=15, justification='right'),
            sg.Input(key='num_level', size=22, tooltip="暂未开发，找刘超", default_text="0000"),
        ],
        [
            sg.Text('  ', size=88, justification='left'),  # 空白区域
            sg.Button(
                "开始",
                bind_return_key=True,
                size=10,
                mouseover_colors='#1d953f',
                use_ttk_buttons=True),
            sg.Button(
                "配置",
                size=10,
                mouseover_colors='#dec674',
                use_ttk_buttons=True,
                tooltip='点击打开配置文件进行配置,右键获取工具源码',
                right_click_menu=['&Right',
                                  right_button_menu]),
        ]
    ]
    layout_result = [
        [
            sg.Output(key='debug_result', size=(125, 20))
        ]
    ]
    layout = [
        [layout_title],
        [sg.Frame('基础数据', layout_basedata, border_width=1, right_click_menu=['&Right', right_button_menu])],
        [sg.Frame('基础配置', layout_baseconfig, border_width=1, right_click_menu=['&Right', right_button_menu])],
        [sg.Frame('', layout_result, border_width=1, right_click_menu=['&Right', right_button_menu])],
    ]
    return layout


class PrpCrypt(object):
    def __init__(self, key="ZTE@FSSCZTE@FSSC"):
        self.key = key.encode('utf-8')
        self.mode = AES.MODE_ECB

    def pad_byte(self, b):
        return pad(b, AES.block_size)

    def encrypt(self, text):
        """
                1 先生成aes实例
                2 对传入的text转成bytes
                3 对传入的text补全
                4 调用encrypt 加密 得到密文
                5 先将密文转16进制，在将16进制用base64转码，然后在将得到的base64解码

                :param text:
                :return:
                """
        cryptor = AES.new(self.key, self.mode)
        text = text.encode('utf-8')
        text = self.pad_byte(text)
        ciphertext = cryptor.encrypt(text)
        cryptbase64 = base64.b64encode(ciphertext)
        cryptbase64 = cryptbase64.decode("utf-8")
        return cryptbase64

    def decrypt(self, text):
        """
                解密和加密的顺序是相反的
                1 定义匿名函数，去掉补位
                2 base64解码
                3 生成aes实例
                4 16进制转2进制
                5 使用decrypt解码  得到补全的bytes类型明文

                :param text:
                :return:  解密且去掉补位的明文
                """
        def unpad(s): return s[:-ord(s[len(s) - 1:])]
        base64Str = base64.b64decode(text.encode('utf8'))
        cryptor = AES.new(self.key, self.mode)
        aesStr = cryptor.decrypt(a2b_hex(base64Str))
        aesStr = str(unpad(aesStr), encoding='utf8')
        return aesStr


class FORMAT_PAPAMS(object):


    def read_json(self):
        with open("test_data.json", "r", encoding="utf-8") as fr:
            return json.loads(fr.read())


    def format_query_params(self, content, flag, pointkey=None, **kwargs):
        """格式化json
        pointkey 作为指定非必填参数列表，逗号隔开 [a, b, c, d]
        """
        global t_windows
        flag = str(flag)
        str2 = []
        result_list = []
        if isinstance(pointkey, str):
            pointkey = pointkey.replace(" ", "").replace("，", ",").split(',')
        if not isinstance(pointkey, list):
            pointkey = []
        for k in content:
            if len(k) >= 4 and k[-4:].lower() == "date":
                sada = f"\"{k}\": datetime.datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")"
                result_list.append(sada)
            elif k == "id":
                sdasd = f"\"{k}\": {k}"
                result_list.append(sdasd)
            elif k == "languages" and isinstance(content.get(k), list):
                languages = content.get(k)
                list21312 = []
                try:
                    if isinstance(languages, list) or isinstance(content.get(k), dict):
                        ads = 1
                        for dict_this in languages:
                            listwqwqd = []
                            for key in dict_this.keys():
                                if key == "language":
                                    if ads == 1:
                                        sdasd = f'\t\t\"{key}\": \"zh-CN\"'
                                        ads = 0
                                    else:
                                        sdasd = f'\t\t"{key}": "en-US"'
                                else:
                                    sdasd = f"\t\t\"{key}\": kwargs.get(\"{key.replace('-', '_')}\")"
                                listwqwqd.append(sdasd)
                            str_lang = "\t{\n" + ",\n".join(listwqwqd) + "\n\t}"
                            list21312.append(str_lang)
                        str_lang = f'"{k}": [\n' + ",\n".join(list21312) + "\n]"
                        result_list.append(str_lang)
                except:
                    sdasd = f"\"{k}\": kwargs.get(\"{k.replace('-', '_')}\")"
                    result_list.append(sdasd)
            else:
                if flag == "1" or (flag != "1" and not content.get(k)) or k in pointkey:
                    if flag == "1":
                        if "extend" in k and not content.get(k):  # 编辑时候，拓展值为null的行直接放弃
                            continue
                        if '-' in k:
                            sdasd = f"\"{k}\": kwargs.get(\"{k.replace('-', '_')}\") or kwargs.get(\"{k}\")"
                        else:
                            sdasd = f"\"{k}\": kwargs.get(\"{k}\")"
                    else:
                        sdasd = f"\"{k}\": kwargs.get(\"{k.replace('-', '_')}\")"
                    result_list.append(sdasd)
                else:
                    sdasd = f"\"{k}\": {k.replace('-', '_')}"
                    result_list.append(sdasd)
                    str2.append(k.replace('-', '_'))
        # 将文本写入剪切板
        sg.popup_quick_message("\n结果已写入剪切板，可直接粘贴\n", background_color='#0099ff')
        text = ",\n".join(result_list)
        pyperclip.copy(text)
        # 输出剪切板的内容
        # text = pyperclip.paste()
        # print(text)
        print(text)

        if len(str2):
            kwargs.get('windows')['must_para'].update(value = f"{', '.join(str2) + ','}")
            # print(f"\n函数必填入参：{', '.join(str2) + ','}\n")



    def format_get_params_to_dict(self, content, flag):
        """处理成字典，flag为0处理成具体的值，flag为1处理成kwargs，为2处理成本身名称变量"""
        flag = str(flag)
        result_list = []
        a = unquote(content).split("&")
        for u, i in enumerate(a):
            i = i.split("=")
            if flag == "0":
                # 具体值
                result_list.append('"{}": "{}"'.format(i[0], i[1]))
            elif flag == "1":
                result_list.append('"{}": {}'.format(i[0], f'kwargs.get("{i[0]}")'))
            else:
                result_list.append('"{}": {}'.format(i[0], i[0]))
        sg.popup_quick_message("\n结果已写入剪切板，可直接粘贴\n", background_color='#0099ff')
        text = ",\n".join(result_list)
        pyperclip.copy(text)
        print(text)


    def GET_VALUE_FROM_JSON_DICT(self, _obj, key):
        """sss
        从响应中根据key获取值(所有)
        :param _obj: 响应结果json
        :param key: key
        :return: 查到的值(list)
        """
        ret = []

        def _get_value_from_json_dict(_obj, _key):
            if isinstance(_obj, list):
                for _i in _obj:
                    _get_value_from_json_dict(_i, _key)
            elif isinstance(_obj, dict):
                for _k, _v in _obj.items():
                    if _k == _key:
                        ret.append(_v)
                    else:
                        _get_value_from_json_dict(_v, _key)
            else:
                return

        _get_value_from_json_dict(_obj, key)

        return ret


    def format(self, content, pointkey=None, **kwargs):
        """pointkey:手动改成非必填的参数"""
        windows = kwargs.get('windows')
        content = content  # .replace("true", "True").replace("false", "False").replace("null", "None").replace("'", '"')
        pointkey = pointkey.replace(" ", "").replace("，", ",").split(',')
        if "&" in content:
            if '&id=' in content:
                flag = '1'  # 编辑
            else:
                flag = '0'  # 新增
            self.format_get_params_to_dict(content, flag)
        else:
            try:
                content = json.loads(content)
            except ValueError as e:
                sg.popup_quick_message("PARAMS需输入json格式，Again Please.", auto_close_duration=5, background_color='#0099ff')
                return False

            if 'id' in content.keys():
                flag = '1'  # 编辑
            else:
                flag = '0'  # 新增

            self.format_query_params(content, flag, pointkey=pointkey, windows=windows)


# 格式化UI
def format_params_ui(data, **kwargs):
    layout_factor = [
        [
            sg.Text('* 场景：', size=5, justification='right'),
            sg.InputCombo(key='bill_type', size=17, default_value=list_bill_type[2],
                          readonly=True, values=list_bill_type,
                          enable_events=True),
            sg.Text('归入非必填参数：', size=13, justification='right'),
            sg.Input(key='pointkey', size=20, tooltip="需要手动过滤为非必填的字段\n逗号隔开")
        ],
        [sg.Text('  ', size=10, justification='left')],
        [sg.Text('* 待格式化请求参数：',
                 size=20,
                 justification='left'),
         ],
        [sg.Multiline(key='params',
                      s=(65, 8),
                      tooltip='[x-www-form-urlencoded] or [JSON] 格式的请求体')],
        [sg.Text('请求体参数：',
                 size=15,
                 justification='left'),
         ],
        [
            sg.Output(key='debug_result', size=(65, 12), echo_stdout_stderr=True)
        ],
        [sg.Text('必填参数:',
                 size=20,
                 justification='left'),
         ],
        [
            sg.InputText(key='must_para', size=(65, 2))
        ],
        [
            sg.Text('  ', size=15, justification='left'),  # 空白区域
            sg.Button(
                "处理",
                bind_return_key=True,
                size=10,
                mouseover_colors='#1d953f',
                use_ttk_buttons=True),
            sg.Button(
                "重置",
                size=10,
                mouseover_colors='#dec674',
                use_ttk_buttons=True,
                tooltip='点击打开配置文件进行配置,右键获取工具源码',
                right_click_menu=['&Right',
                                  right_button_menu]),
            sg.Text('  ', size=10, justification='left'),  # 空白区域
        ],

    ]
    layout = [
        [
            sg.Frame(' ', layout_factor, border_width=0, right_click_menu=['&Right', right_button_menu])]
    ]
    return layout


# 公司部门岗位员工UI
def tool_ui_set(data, **kwargs):
    layout_factor = [
        [sg.Text('  ', size=10, justification='left')],
        [
            sg.Text('* 测试环境：', size=15, justification='right'),
            sg.InputCombo(key='base_url', size=20, default_value=test_env, values=data.get('list_test_env') or list_test_env,
                          readonly=True, enable_events=True),
        ],
        [sg.Text('  ', size=10, justification='left')],
        [
            sg.Text('* 场景：', size=15, justification='right'),
            sg.InputCombo(key='bill_type', size=20, default_value=list_bill_type[1], values=list_bill_type,
                          enable_events=True, readonly=True)
        ],
        [sg.Text('  ', size=10, justification='left')],
        [
            sg.Text('公司父节点Name/Code：', size=15, justification='right'),
            sg.Input(key='root', size=22, default_text="", tooltip="以companyName/companyCode格式写入\n为空则默认集团节点")
        ],
        [sg.Text('  ', size=10, justification='left')],
        [
            sg.Text('* 公司节点数：', size=15, justification='right'),
            sg.Input(key='comp_num', size=22, default_text="5"),
        ],
        [sg.Text('  ', size=10, justification='left')],
        [
            sg.Text('公司下部门数：', size=15, justification='right'),
            sg.Input(key='dept_num', size=22, default_text=""),
        ],
        [sg.Text('  ', size=10, justification='left')],
        [
            sg.Text('部门下岗位数：', size=15, justification='right'),
            sg.Input(key='post_num', size=22, default_text=""),
        ],
        [sg.Text('  ', size=10, justification='left')],
        [
            sg.Text('岗位下员工数：', size=15, justification='right'),
            sg.Input(key='emp_num', size=22, default_text=""),
        ],
        [sg.Text('  ', size=10, justification='left')],
        [
            sg.Text('末尾特征标记：', size=15, justification='right'),
            sg.Input(key='namedflag', size=22, default_text="autoTest_"),
        ],
        [sg.Text('  ', size=10, justification='left')],
        [
            sg.Text('  ', size=5, justification='left', right_click_menu=['&Right', right_button_menu]),  # 空白区域
            sg.Button(
                "创建",
                bind_return_key=True,
                size=10,
                mouseover_colors='#1d953f',
                use_ttk_buttons=True),
            sg.Button(
                "配置",
                size=10,
                mouseover_colors='#dec674',
                use_ttk_buttons=True,
                tooltip='点击打开配置文件进行配置,右键获取工具源码',
                right_click_menu=['&Right',
                    right_button_menu]),
            sg.Text('  ', size=10, justification='left'),  # 空白区域
        ],
        [sg.Text('  ', size=10, justification='left')],

    ]
    layout = [
        [
         sg.Frame(' ', layout_factor, border_width=0, right_click_menu=['&Right', right_button_menu])]
    ]
    return layout


# 提单主界面框架
def tool_ui(data):
    """
        工具所有的UI布局部分
        :param data: 传入从配置文件tapdconf.ini读取的数据
        :return:布局layout
        """
    """顶部条件输入区域"""
    global list_test_env, list_user_submit
    try:
        if len(eval(data['list_test_env'])):
            list_test_env = eval(data['list_test_env'])
        if len(eval(data['list_user_submit'])):
            list_user_submit = eval(data['list_user_submit'])
    except:
        sg.popup("配置文件中'list_test_env'必须为list")
        return

    InputSize = 19
    InputCombo_size = InputSize - 2
    layout_factor = [
        [
            sg.Text('* 测试环境：', size=10, justification='right'),
            sg.InputCombo(key='base_url', size=InputCombo_size, default_value=test_env, values=list_test_env,
                          enable_events=True, change_submits=True, tooltip='手动添加新的环境：\n界面新增后需点击暂存按钮\n或者点击配置修改list_test_env后右键刷新'),
            sg.Text('* 场景：', size=10, justification='right'),
            sg.InputCombo(key='bill_type', size=InputCombo_size, default_value=list_bill_type[0], values=list_bill_type,
                          readonly=True, enable_events=True),
            sg.Text('* 测试账号：', size=10, justification='right'),
            sg.InputCombo(key='user_submit', size=InputCombo_size, default_value=data['user_submit'], values=list_user_submit,
                          enable_events=True),
            sg.Text('* 测试密码：', size=10, justification='right'),
            sg.Input(
                key='passwd_submit',
                size=InputSize,
                default_text=data['passwd_submit']),
        ],
        [
            sg.Text('* 数据终点：', size=10, justification='right'),
            sg.InputCombo(key='destination',
                          size=InputCombo_size,
                          default_value=data.get('destination') if data.get('destination') in destination_list else "",
                          readonly=True,
                          values=destination_list),
            sg.Text('共享名称：', size=10, justification='right'),
            sg.InputCombo(key='nodename',
                          size=InputCombo_size,
                          default_value=data.get('nodename') or '共享中心',
                          values=['共享中心', '共享审批', '共享中心审批', '财务审批']),
            sg.Text('次数：', size=10, justification='right'),
            sg.Input(key='times', size=InputSize, default_text=data['times'] or '1', tooltip='提多少单？'),
            sg.Text('* 线程数：', size=10, justification='right'),
            # sg.Spin([i for i in range(1, 51)], initial_value=10, key='MaxWorkers', readonly=True, size=(4, 1)),
            sg.Slider(key='MaxWorkers', range=(1, 12), default_value=5, size=(InputCombo_size-3, 10),
                      orientation='horizontal', font=('Helvetica', 11), expand_y=True),
        ],
        # 按钮行
        [
            sg.Button(
                "获取支付指令",
                size=12,
                mouseover_colors='#dec674',
                use_ttk_buttons=True,
                tooltip="根据生成的单据列表获取支付指令,右键获取工具源码"),
            sg.Text('  ', size=58, justification='left', right_click_menu=['&Right', right_button_menu]),  # 空白区域
            sg.Button(
                "执行",
                bind_return_key=True,
                size=10,
                mouseover_colors='#1d953f',
                use_ttk_buttons=True,
                tooltip='发票会被换成增值税电子普通发票'),
            sg.Button(
                "暂存",
                size=10,
                mouseover_colors='#dec674',
                use_ttk_buttons=True,
                tooltip='暂存界面输入内容'),
            sg.Button(
                "查看",
                size=10,
                mouseover_colors='#dec674',
                use_ttk_buttons=True,
                tooltip='查看生成的数据'),
            sg.Button(
                "配置",
                size=10,
                mouseover_colors='#dec674',
                use_ttk_buttons=True,
                tooltip='点击打开配置文件进行配置,右键获取工具源码',
                right_click_menu=['&Right', right_button_menu]),
        ]
    ]
    """结果左侧字段"""
    layout_left = [[sg.Text('* 单据请求体：',
                            size=50,
                            justification='left'),
                    ],
                   [sg.Multiline(key='boe_body',
                                 s=(55,
                                    24),
                                 tooltip='F12取提单接口(submitDraftBoe)的JSON格式的请求体',
                                 default_text=data['boe_body'])],
                   ]
    """工具执行结果展示控件"""
    layout_result = [
        [
            sg.Output(key='debug_result', size=(70, 26))
        ]
    ]
    """总体布局容器"""
    layout = [
        [
            sg.Frame(key='frame1', title='', layout=layout_factor, right_click_menu=['&Right', right_button_menu])
        ],
        [
            sg.Frame(' 单据信息：', layout_left, right_click_menu=['&Right', right_button_menu]),
            sg.Frame(' 执行日志：', layout_result, right_click_menu=['&Right', right_button_menu]),
        ],

    ]
    return layout


# 读配置文件
def read_conf():
    """
        读配置文件
        :return: tmp=config.items("TAPD");a2
        """
    global path_pic, test_env, tool_list

    test_env = choice_env(flag="read")

    path = path_pic + rf'CHN_TGtools({test_env.split("//")[-1]}).ini'  # 配置文件路径

    if not os.path.exists(path):  # 判断文件是否存在
        if not os.path.exists(path_pic):  # 判断文件夹是否存在
            os.makedirs(path_pic)
        tapdconf_reset(path)  # 重置配置文件
    else:
        with open(path, 'r') as f:
            conf_text = f.read()
        if len(conf_text.strip()) > 0:  # 判断是否空文件
            config.read(path)
            list_sections = config.sections()
            # 没这个类  # 参数不一样
            if 'CTG' not in list_sections or set(config.options('CTG')) - set(config_list) != set(tool_list):
                # print(f">>># 没这个类  # 参数不一样:{set(config.options('CTG')) - set(config_list)}\n{set(tool_list)}")
                tapdconf_reset(path)  # 重置配置文件
        else:  # 空文件
            tapdconf_reset(path)  # 重置配置文件
    with open(path, 'r') as f:
        config.read(f)
        tmp = config.items("CTG")  # 列表元组
        a2 = {}
        for k, v in tmp:
            a2[k] = v
        return a2


# 重置配置文件
def tapdconf_reset(path):
    """如果配置文件被改,调此方法直接重置配置文件"""
    config["CTG"] = {
        'nodename': '',
        'bill_type': list_bill_type[0],
        'times': '1',
        'destination': destination_list[-1],
        'list_test_env': list_test_env,
        'user_submit': '20210911001',
        'list_user_submit': list_user_submit,
        'passwd_submit': 'zfs123456',
        'boe_body': '',
    }
    with open(path, 'w') as f:
        config.write(f)


def choice_env(flag="read", content=None):
    if content is None:
        content = list_test_env[0]  # 为空就取生产
    if flag == "write":
        with open(env_path, 'w') as f:
            f.write(content)
    elif flag == "read":
        if not os.path.exists(env_path):  # 判断文件是否存在
            if not os.path.exists(path_pic):  # 判断文件夹是否存在
                os.makedirs(path_pic)
            with open(env_path, 'w') as f:
                f.write(list_test_env[0])
        with open(env_path, "r") as f:
            return f.read().strip("\n").strip(" ").strip("\t")


# 记录测试环境，选择对应的配置文件
def save_his_path(his_baseUrl, path_contentpanel, search_path):
    """保存搜索过的路径到his_baseUrl。路径存在且尚未保存过才保存,最多存十条数据"""
    with open(his_baseUrl, "r") as f:  # his_baseUrl.txt,存储10个历史查询的路径
        list1 = f.read().split("\n")
        Initial_list1 = list1
        list1 = list1[len(list1) - 10 if len(list1) > 10 else 0:]
    if search_path not in list1:
        if list1[0] == '':
            list1[0] = search_path
        else:
            list1.append(search_path)
        with open(his_baseUrl, "w") as f:
            f.write("\n".join(list1[len(list1) - 10 if len(list1) > 10 else 0:]))
            if len(Initial_list1[0]):  # 如果下拉列表是空
                path_contentpanel.Append(list1[-1])  # 追加到地址框下拉列表
            else:  # 写到第一个
                path_contentpanel.Clear()
                path_contentpanel.Append(list1[-1])
                path_contentpanel.SetValue(list1[-1])


def path_contentpanel_method(path_contentpanel, his_baseUrl):
    with open(his_baseUrl, "r") as f:  # his_baseUrl.txt,存储10个历史查询的路径
        list2 = f.read().split("\n")
        list2 = list2[len(list2) - 10 if len(list2) > 10 else 0:]
        # print("重新获取下拉列表1", self.list2)
    path_contentpanel.UpdateValue = list2


# 写配置文件
def write_conf(path, section, option, value=None):
    """写配置文件"""
    config.set(section, option, value)  # 写配置文件
    with open(path, 'w') as f:
        config.write(f)
    # print("更新配置文件[%s]" % option)


# 模糊匹配方法 a1:原列表,a2:过滤字段,返回过滤后的字段
def search_result(a1, a2):
    return list((filter(lambda seq: re.search(a2, seq) is not None, a1)))


# 输入框格式检查
def input_check(key_data):
    try:
        if key_data.isdigit() is False:  # 不是纯数字
            return True
        return False
    except BaseException:
        return False


# 测试开始时间校验
def date_check(date):
    """
        校验date格式为 YYYY-mm-dd，且早于当天日期。超过120天弹提示框。
        如未符合以上所有条件，返回True
        """
    try:
        today = datetime.datetime.today().strftime('%Y-%m-%d')  # 获取今日日期:str
        today = datetime.datetime.strptime(today, "%Y-%m-%d")  # str转datetime
        if len(date) == 10:
            r = re.compile('^\\d{4}-\\d{2}-\\d{2}')  # 正则规则，yyyy-mm-dd
            if r.match(date) is None:
                sg.popup("请确认[测试开始时间]格式正确", title='提示')
                return True
        else:
            sg.popup("请确认[测试开始时间]格式正确", title='提示')
            return True
        start_time_datetime = datetime.datetime.strptime(
            date, "%Y-%m-%d")  # str转datetime
        if (start_time_datetime - today).days > 0:
            sg.popup("[测试开始时间]请选择今日之前的日期", title='提示')
            return True
        if (start_time_datetime - today).days < -120:
            button_return = sg.popup_ok_cancel(
                "测试开始日期距今超过120天，\n会影响图表展示效果，\n是否继续?")
            if button_return == 'Cancel' or button_return is None:
                return True
    except Exception:
        sg.popup("请检查[测试开始时间]格式是否正确", title='提示')


# 将界面字段值写入配置文件
def save_data(values, a2, flag_UpdateConf=None):
    """
        :param values: 界面数据
        :param a2: 配置文件数据
        :param flag_UpdateConf:
        :return:
        """
    global path_pic, test_env, tool_list
    test_env = choice_env(flag="read")
    path = path_pic + rf'CHN_TGtools({test_env.split("//")[-1]}).ini'  # 配置文件路径

    for key in values:
        if key in ['c1', 'c2']:
            values[key] = str(values[key])
        if key not in tool_list:
            continue
        elif values[key] == a2[key]:  # 界面内容vals2与配置文件内容a2一致
            continue
        else:
            # print("更新的key:", key)
            config.set("CTG", key, str(values[key]))  # 写入配置文件
            a2[key] = str(values[key])
            flag_UpdateConf = True
    if flag_UpdateConf:
        with open(path, 'w') as f1:
            config.write(f1)
    print(
        "[%s]界面信息已成功保存，当前数据在工具启动时可自动恢复。" %
        time.strftime(
            "%Y-%m-%d %H:%M:%S",
            time.localtime()))
    return a2


# 登录
def login(user_name, password, **kwargs):
    """
        共享系统获取cookie
        :param serverIp:
        :param user_name:
        :param password:明文密码即可
        :param protocol:
        :param port:
        :param login_url:
        :param args:
        :param kwargs:
        :return:
        """
    try:
        if not base_url:
            sg.popup_quick_message("登录出现异常，请检查base_url", title="异常", background_color='#0099ff')
            return False
        login_url = str(base_url) + '/sys/auth/login'
        headers = {
            "Content-Type": "application/x-www-form-urlencoded"
        }
        data = {
            "loginKey": user_name,
            "password": PrpCrypt().encrypt(password),
            "multiGroup": "true",
            "loginPlatform": "PC"
        }


        response = requests.post(url=login_url, headers=headers, data=data)
        if "success" not in response.text:
            print(f"【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 登陆失败\n    接口响应：{response.text}")
            sg.popup_quick_message("登录出现异常，请检查测试环境、账号信息", title="异常", background_color='#0099ff')
            return False

        cookies = response.cookies.get_dict()
        cookie = f"{list(cookies.keys())[0]}={list(cookies.values())[0]}"
        return cookie
    except Exception as error:
        sg.popup_quick_message("登录出现异常，请检查测试环境", title="异常", background_color='#0099ff')
        return False


# 初始化单据
def _init_boe(cookie, boeTypeCode):
    """
        初始化单据
        :param cookie:
        :param boeTypeCode:单据类型编码
        :return:
        """
    api = "/sys/boe/core/initBoePage"
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "Cookie": cookie
    }
    data = {
        "boeTypeCode": boeTypeCode
    }
    response = None
    try:
        response = requests.post(
            url=base_url + api,
            headers=headers,
            data=data,
            verify=False)
        assert "success" in response.text
        return response.json()
    except Exception as error:
        if isinstance(error, AssertionError):
            print(f"'单据初始化'接口响应:{response.text}\n", end='')
        else:
            print(f"'单据初始化'接口异常信息：{str(error)}\n", end='')
        print('')
        return False


# 新增其他纸质票发票
def _add_invoice_other(cookie, ticketDefineTemplateId, billList):
    api = "/sys/boe/boeInvoiceTicket/save"
    today = datetime.datetime.today().strftime('%Y-%m-%d')  # 获取今日日期:str
    headers = {
        "Cookie": cookie,
        "Content-Type": "application/json"
    }
    ticketDataList = []
    count = 0
    flag = 0
    while True:
        fee = billList[count].get("fee")
        operationSubTypeId = billList[count].get("operationSubTypeId")
        userId = billList[count].get("userId")
        groupId = billList[count].get("userId")
        body = {
            "billTypeInvoice": "3",
            "invoiceCount": 1,
            "fee": fee,
            "billingTime": today,
            "remarks": "",
            "ticketData": {
                "modelType": "2",
                "invoiceCount": 1,
                "fee": fee,
                "billingTime": today,
                "remarks": ""},
            "ticketDefineTemplateId": ticketDefineTemplateId
        }

        r = requests.post(url=base_url + api, headers=headers, json=body)
        try:
            billid = r.json()["data"]["id"]
            billTicket = {
                "fee": fee,
                "billTypeInvoice": "3",
                "billTypeInvoiceName": "其他纸质票",
                "dataSource": "2",
                "dataSourceName": "报账系统",
                "reimbursementState": "1",
                "reimbursementStateName": "未报销",
                "validState": "004",
                "validStateName": "无需查验",
                "authState": "RZZT0002",
                "authDate": None,
                "authFailedMsg": None,
                "authStateName": "无需认证",
                "bigImageUrl": None,
                "smallImageUrl": None,
                "holderId": userId,
                "holderName": "批量提单人",
                "holderCode": "20210911001",
                "companySealName": None,
                "ticketDefineTemplateId": ticketDefineTemplateId,
                "vehicleName": None,
                "postState": "RZZT01",
                "boeTypeId": None,
                "boeTypeName": None,
                "postStateName": "未入账",
                "extendS1": None,
                "extendS2": None,
                "extendS3": None,
                "extendS4": None,
                "extendS5": None,
                "extendS6": None,
                "extendS7": None,
                "extendS8": None,
                "extendS9": None,
                "extendS10": None,
                "extendNumber1": None,
                "extendNumber2": None,
                "extendNumber3": None,
                "extendNumber4": None,
                "extendNumber5": None,
                "extendNumber6": None,
                "extendNumber7": None,
                "extendNumber8": None,
                "extendNumber9": None,
                "extendNumber10": None,
                "validityFlag": "0",
                "enabledFlag": "0",
                "groupId": groupId,
                "createBy": userId,
                "createName": "批量提单人",
                "createDate": today,
                "lastUpdateBy": userId,
                "lastUpdateDate": today,
                "archiveFlag": None,
                "archiveDate": None,
                "boeNo": None,
                "voucherNum": None,
                "operationTypeCode": None,
                "operationTypeName": None,
                "operationSubTypeCode": "TEST3801",
                "operationSubTypeName": "批量提单小类",
                "leId": None,
                "leName": None,
                "voucherDate": None,
                "orderDate": today,
                "ticketDate": f"{today} 00:00:00",
                "diffExpireDays": None,
                "isReimburse": "Y",
                "userId": userId,
                "userName": "批量提单人",
                "entrySaleItemName": "进项",
                "cityName": None,
                "personnelAttributionName": None,
                "seatLevelName": None,
                "obsoleteState": None,
                "obsoleteStateName": None,
                "actualAmount": None,
                "ticketSaveSource": "1",
                "ticketSaveSourceName": None,
                "buyerValidState": None,
                "passenger": None,
                "personnelAttribution": None,
                "empNo": "20210911001",
                "empName": None,
                "isReplacementTicketName": None,
                "opTicketDetailsList": None,
                "ticketData": {
                    "lastUpdateDate": today,
                    "fee": fee,
                    "isReimburse": "Y",
                    "groupId": groupId,
                    "validityFlag": "0",
                    "billTypeInvoice": "3",
                    "enabledFlag": "0",
                    "reimbursementState": "1",
                    "ticketSaveSource": "1",
                    "ticketDefineTemplateId": ticketDefineTemplateId,
                    "validState": "004",
                    "createDate": today,
                    "authState": "RZZT0002",
                    "invoiceCount": 1,
                    "billingTime": today,
                    "postState": "RZZT01",
                    "modelType": "2",
                    "buyerName": "000",
                    "holderId": userId,
                    "userId": userId,
                    "createBy": userId,
                    "taxDeductible": 0,
                    "_id": billid,
                    "entrySaleItem": "0",
                    "orderDate": today,
                    "dataSource": "2",
                    "lastUpdateBy": userId
                },
                "oilFee": None,
                "ticketPrice": None,
                "taxDeductible": 0,
                "fileName": None,
                "invoiceCount": 1,
                "billingTime": today,
                "modelType": "2",
                "buyerName": "000",
                "_id": billid,
                "entrySaleItem": "0",
                "isRelevance": "0",
                "expenseAmount": fee,
                "expenseDate": today,
                "referenceId": billid,
                "referenceType": "3",
                "isPay": "0",
                "excessiveReason": "",
                "invoiceTypeCode": "3",
                "invoiceTypeName": "其他纸质票",
                "employeeId": {
                    "isTrusted": True
                },
                "attribute": None,
                "operationSubTypeId": operationSubTypeId,
                "guid": "989",
                "extendMap": {
                    "validState": "004",
                    "validStateName": "无需查验",
                    "fee": fee,
                    "invoiceTypeCode": "3",
                    "attribute": None,
                    "invoiceCount": 1,
                    "excessiveReason": ""
                },
                "lineNum": count,
                "lineType": "cost",
                "isGenVoucher": "0"
            }
        except Exception:
            flag += 1
            if flag > 2:
                break
        else:
            count += 1
            ticketDataList.append(billTicket)
            if count == len(billList):
                break

    return ticketDataList

# 新增增值税电子普通发票
def _add_invoice(cookie, ticketDefineTemplateId, billList):
    api = "/sys/boe/boeInvoiceTicket/save"
    today = datetime.datetime.today().strftime('%Y-%m-%d')  # 获取今日日期:str
    headers = {
        "Cookie": cookie,
        "Content-Type": "application/json"
    }
    ticketDataList = []
    count = 0
    flag = 0
    while True:
        fee = billList[count].get("fee")
        operationSubTypeId = billList[count].get("operationSubTypeId")
        userId = billList[count].get("userId")
        groupId = billList[count].get("userId")
        billingNo = str(random.randint(int(1e+11), int(1e+12)-1))
        billingCode = str(random.randint(int(1e+7), int(1e+8)-1))
        checkCode = str(random.randint(int(1e+3), int(1e+8)-1))
        body = {
                "billTypeInvoice": "17",
                "billingNo": billingNo,
                "billingCode": billingCode,
                "billingTime": today,
                "checkCode": checkCode,
                "fee": fee,
                "tax": 0,
                "feeWithoutTax": fee,
                "details": "",
                "opTicketDetailsList": [
                        {
                                "goodserviceName": "",
                                "billTypeInvoice": "17",
                                "ticketDetailsData": {
                                        "goodserviceName": ""
                                }
                        }
                ],
                "ticketData": {
                        "modelType": "1",
                        "billingNo": billingNo,
                        "billingCode": billingCode,
                        "billingTime": today,
                        "checkCode": checkCode,
                        "fee": fee,
                        "tax": 0,
                        "feeWithoutTax": fee
                },
                "ticketDefineTemplateId": ticketDefineTemplateId
        }
        r = requests.post(url=base_url + api, headers=headers, json=body)
        try:
            billid = r.json()["data"]["id"]
            businessAttribute = "小类属性"
            billTicket = {
                        "fee": fee,
                        "billTypeInvoice": "17",
                        "billTypeInvoiceName": "增值税电子普通发票",
                        "dataSource": "2",
                        "dataSourceName": "报账系统",
                        "reimbursementState": "1",
                        "reimbursementStateName": "未报销",
                        "validState": "001",
                        "validStateName": "待查验",
                        "authState": "RZZT0002",
                        "authDate": None,
                        "authFailedMsg": None,
                        "authStateName": "无需认证",
                        "bigImageUrl": None,
                        "smallImageUrl": None,
                        "holderId": userId,
                        "holderName": "xxx",
                        "holderCode": "190201",
                        "companySealName": None,
                        "ticketDefineTemplateId": ticketDefineTemplateId,
                        "vehicleName": None,
                        "postState": "RZZT01",
                        "boeTypeId": None,
                        "boeTypeName": None,
                        "sysTaxRateDTO": None,
                        "deductibleItem": None,
                        "postStateName": "未入账",
                        "extendS1": None,
                        "extendS2": None,
                        "extendS3": None,
                        "extendS4": None,
                        "extendS5": None,
                        "extendS6": None,
                        "extendS7": None,
                        "extendS8": None,
                        "extendS9": None,
                        "extendS10": None,
                        "extendNumber1": None,
                        "extendNumber2": None,
                        "extendNumber3": None,
                        "extendNumber4": None,
                        "extendNumber5": None,
                        "extendNumber6": None,
                        "extendNumber7": None,
                        "extendNumber8": None,
                        "extendNumber9": None,
                        "extendNumber10": None,
                        "validityFlag": "0",
                        "enabledFlag": "0",
                        "groupId": groupId,
                        "createBy": userId,
                        "createName": "tester",
                        "createDate": f"{today} 01:01:01",
                        "lastUpdateBy": userId,
                        "lastUpdateDate": f"{today} 01:01:01",
                        "archiveFlag": None,
                        "archiveDate": None,
                        "boeNo": None,
                        "voucherNum": None,
                        "operationTypeCode": None,
                        "operationTypeName": None,
                        "operationSubTypeCode": "12220101",
                        "operationSubTypeName": "业务小类1",
                        "leId": None,
                        "leName": None,
                        "voucherDate": None,
                        "orderDate": today,
                        "ticketDate": f"{today} 00:00:00",
                        "diffExpireDays": None,
                        "isReimburse": "Y",
                        "userId": userId,
                        "userName": "tester",
                        "entrySaleItemName": "进项",
                        "cityName": None,
                        "personnelAttributionName": None,
                        "seatLevelName": None,
                        "obsoleteState": "5",
                        "obsoleteStateName": "正常",
                        "actualAmount": None,
                        "ticketSaveSource": "1",
                        "ticketSaveSourceName": None,
                        "buyerValidState": None,
                        "passenger": None,
                        "personnelAttribution": None,
                        "empNo": "190201",
                        "empName": None,
                        "isReplacementTicketName": None,
                        "opTicketDetailsList": [
                            {
                                "id": "55ee3a94ffff881df90fc1c7b1b9b2ae00c1",
                                "allTicketId": billid,
                                "allTicketDate": f"{today} 00:00:00",
                                "taxSum": None,
                                "reimburseAmount": None,
                                "invoiceBalanceAmountSum": None,
                                "vehicleName": None,
                                "seatLevelName": None,
                                "extendS1": None,
                                "extendS2": None,
                                "extendS3": None,
                                "extendS4": None,
                                "extendS5": None,
                                "extendS6": None,
                                "extendS7": None,
                                "extendS8": None,
                                "extendS9": None,
                                "extendS10": None,
                                "extendNumber1": None,
                                "extendNumber2": None,
                                "extendNumber3": None,
                                "extendNumber4": None,
                                "extendNumber5": None,
                                "extendNumber6": None,
                                "extendNumber7": None,
                                "extendNumber8": None,
                                "extendNumber9": None,
                                "extendNumber10": None,
                                "validityFlag": "0",
                                "enabledFlag": "0",
                                "groupId": groupId,
                                "createBy": userId,
                                "createDate": f"{today} 01:01:01",
                                "lastUpdateBy": userId,
                                "lastUpdateDate": f"{today} 01:01:01",
                                "archiveFlag": None,
                                "archiveDate": None,
                                "airNumber": None,
                                "passengerName": None,
                                "ticketDetailsData": {
                                    "createBy": userId,
                                    "lastUpdateDate": f"{today} 01:01:01",
                                    "groupId": groupId,
                                    "validityFlag": "0",
                                    "enabledFlag": "0",
                                    "_id": "55ee3a94ffff881df90fc1c7b1b9b2ae00c1",
                                    "allTicketId": billid,
                                    "createDate": f"{today} 01:01:01",
                                    "lastUpdateBy": userId
                                }
                            }
                        ],
                        "ticketData": {
                            "feeWithoutTax": fee,
                            "lastUpdateDate": f"{today} 01:01:01",
                            "fee": fee,
                            "isReimburse": "Y",
                            "groupId": groupId,
                            "validityFlag": "0",
                            "billTypeInvoice": "17",
                            "enabledFlag": "0",
                            "reimbursementState": "1",
                            "ticketSaveSource": "1",
                            "ticketDefineTemplateId": ticketDefineTemplateId,
                            "validState": "001",
                            "createDate": f"{today} 01:01:01",
                            "authState": "RZZT0002",
                            "billingTime": today,
                            "postState": "RZZT01",
                            "billingNo": billingNo,
                            "tax": 0,
                            "modelType": "1",
                            "holderId": userId,
                            "userId": userId,
                            "whetherInvoice": "0",
                            "checkCode": checkCode,
                            "billingCode": billingCode,
                            "createBy": userId,
                            "taxDeductible": 0,
                            "obsoleteState": "5",
                            "_id": billid,
                            "entrySaleItem": "0",
                            "orderDate": today,
                            "dataSource": "2",
                            "lastUpdateBy": userId
                        },
                        "oilFee": None,
                        "ticketPrice": None,
                        "taxDeductible": 0,
                        "fileName": None,
                        "feeWithoutTax": fee,
                        "billingTime": today,
                        "billingNo": billingNo,
                        "tax": 0,
                        "modelType": "1",
                        "whetherInvoice": "0",
                        "checkCode": checkCode,
                        "billingCode": billingCode,
                        "_id": billid,
                        "entrySaleItem": "0",
                        "isRelevance": "0",
                        "expenseAmount": fee,
                        "expenseDate": today,
                        "referenceId": billid,
                        "referenceType": "17",
                        "isPay": "0",
                        "excessiveReason": "",
                        "invoiceTypeCode": "17",
                        "invoiceTypeName": "增值税电子普通发票",
                        "employeeId": {
                            "isTrusted": True
                        },
                        "guid": "559",
                        "reduceAmount": 0,
                        "reimbursementTax": 0,
                        "operationSubTypeId": operationSubTypeId,
                        "businessAttribute": businessAttribute,
                        "attribute": businessAttribute,
                        "apportionType": None,
                        "extendMap": {
                            "billingNo": billingNo,
                            "billingCode": billingCode,
                            "tax": 0,
                            "validState": "001",
                            "validStateName": "待查验",
                            "fee": fee,
                            "invoiceTypeCode": "17",
                            "attribute": businessAttribute,
                            "excessiveReason": "",
                            "reduceAmount": 0
                        },
                        "lineNum": count,
                        "lineType": "cost",
                        "isGenVoucher": "0"
                    }

        except Exception:
            flag += 1
            if flag > 2:
                break
        else:
            count += 1
            ticketDataList.append(billTicket)
            if count == len(billList):
                break

    return ticketDataList

# 提交单据
def _submit_draft_boe(cookie, data, huidiao="0"):
    """
        提交单据
        :param cookie:
        :param data:请求体，json
        :return:response.json()
        """
    global Number_Submit
    api = "/sys/boe/core/submitDraftBoe"
    headers = {
        "Content-Type": "application/json",
        "Cookie": cookie
    }
    for key, value in data.get("boeFormDto").items():
        if isinstance(value, dict):
            if value.get("id"):
                value.pop("id")
        elif isinstance(value, list):
            for i in value:
                if isinstance(i, dict):
                    if i.get("id"):
                        i.pop("id")
    try:
        response = requests.post(
            url=base_url + api,
            headers=headers,
            json=data,
            verify=False)
        if "could not execute statement" in response.text:
            result = True
            if huidiao == '0':
                result = _submit_draft_boe(cookie, data, huidiao='1')
            if not (result and huidiao == '1'):  # 回调时失败才会进去
                print(f"提单[{Number_Submit+1}]出错 当前测试环境数据库无力负载当前并发 造成锁表，降低'线程数'可优化\n", end='')
            return False
        if "success" not in response.text:
            print(f"提单[{Number_Submit+1}]出错 接口响应[2]：{response.text}\n", end='')
            return False
    except Exception as error:
        print(f"提单[{Number_Submit+1}]出错 异常信息[2]：{error}\n", end='')
        return False
    return response.json()


# 获取发票模板
def getTemplate(cookie, ticketType=3):
    url = base_url + "/sys/base/ticketdefine/getTemplate"
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "Cookie": cookie
    }
    data = {
        "ticketType": ticketType,
    }
    r = requests.post(url, headers=headers, data=data)
    assert "success" in r.text
    return r.json().get("data").get("id")


# 抠提单参数中的发票(cost)和费用行(apportion)
def replace_submit_invoice_apportion_data(
        submitData,
        newInvoiceData: list = None,
        newApportion: list = None) -> json:
    if (not newInvoiceData) and (not newApportion):
        return submitData
    boeForm = submitData["boeFormDto"]
    oldCost = copy.deepcopy(boeForm["cost"])
    oldApportion = copy.deepcopy(boeForm["apportion"])
    oldBoeLines = copy.deepcopy(boeForm["zfsBoeLines"])
    if newInvoiceData:
        boeForm["cost"] = newInvoiceData
        for idxI, i in enumerate(oldCost):
            for idxJ, j in enumerate(oldBoeLines):
                if i == j:
                    boeForm["zfsBoeLines"][idxJ] = newInvoiceData[idxI]
                    break
    if newApportion:
        boeForm["apportion"] = newApportion
        for idxI, i in enumerate(oldApportion):
            for idxJ, j in enumerate(oldBoeLines):
                if i == j:
                    boeForm["zfsBoeLines"][idxJ] = newApportion[idxI]
                    break
    return submitData


# 输出控制台内容到GUI
def Output_print_ths(text):
    text = win_main['debug_result'].get() + str(text)
    win_main['debug_result'].Update(value=text)


# 单据提交总流程[员工借款单]
def submit_2(cookie, boe_body: str, goal_boe):
    """
        单据提交总流程[员工借款单]
        #初始化 boeTypeCode=EMPLOYEE_LOANS_BOE&operationTypeId=8f028d3d6385d469d199b1b2b7ae007a
        需更新 data 和 单据类型编码
        :param cookie:
        :return:
        """
    global Number_Submit
    init_res = _init_boe(cookie, 'EMPLOYEE_LOANS_BOE')
    if init_res is False:  # 初始化异常
        return False
    boeNo_sub = init_res.get("data").get("boeNo")  # 初始化提单的单号
    boe_body = boe_body.replace(goal_boe, boeNo_sub)
    # boe_body = re.sub(r'"boeNo":[ ]?"(.+?)",', boeNo_sub, boe_body)
    data = eval(boe_body)
    billList = data.get("boeFormDto").get("cost")
    if billList:
        # ticketDefineTemplateId = "ac4c2f52-1660-574c-e053-0100007f8195"
        newBillList = _add_invoice(cookie, ticketDefineTemplateId, billList)
        data = replace_submit_invoice_apportion_data(data, newBillList)
        # with open("./ttt.json", "w", encoding="utf-8") as f:
        #     json.dump(data, f, ensure_ascii=False)
    try:
        submit_draft_boe = _submit_draft_boe(cookie, data)
        if submit_draft_boe is not False:
            print(f"提单[{Number_Submit+1}]成功 单据编号：{boeNo_sub}\n", end='')
            # cur.execute(f"INSERT INTO submited_boeNo VALUES ('{boeNo_sub}')")
            TaskId = GetTaskList(cookie, boeNo_sub)[0]
            # TaskId = submit_draft_boe.get("data").get("boeFormDto").get("boeHeader").get("wfTaskId")
            d = {
                "boeNo": boeNo_sub,
                "taskId": TaskId
            }
            q.put(d)
            return TaskId, boeNo_sub
        else:
            # print(f"单据提交失败 单据编号：{boeNo}")
            return False
    except Exception as e:
        return False
    finally:
        Number_Submit += 1  # 记录的提单次数加一

# 流程监控将单据跳转至共享审批节点

def GET_VALUE_FROM_JSON_DICT(_obj, key, **kwargs):
    """
    从响应中根据key获取值(所有)
    :param _obj: 响应结果json
    :param key: 目标key
    :param **kwargs: 增加筛选条件，字典中满足kwarg传入的‘查询条件key’=‘value’时，才取‘目标key’对应的值，否则忽略
    :return: 查到的值(list)
    """
    ret = []

    def _get_value_from_json_dict(_obj, _key, **kwargs):
        if isinstance(_obj, list):
            for _i in _obj:
                _get_value_from_json_dict(_i, _key, **kwargs)
        elif isinstance(_obj, dict):
            flag = True
            for kwargsK, kwargsV in kwargs.items():
                if _obj.get(kwargsK) != kwargsV:
                    flag = False
            for _k, _v in _obj.items():
                if _k == _key:
                    if flag:
                        ret.append(_v)
                else:
                    _get_value_from_json_dict(_v, _key, **kwargs)
        else:
            return

    _get_value_from_json_dict(_obj, key, **kwargs)

    return ret


# 在我的单据菜单，获取单据状态为[boeStatus]的boeNo_list信息
def Get_Boe_List_for_fsc(cookie, **kwargs):
    """
        boeStatus: 25-财务初审；26-财务复审
        :param cookie:
        :param kwargs:
        :return:boeNo_List
        """

    # 我的单据菜单获取boe审批信息
    def get_boeNoList_for_fsc(cookie, page='1', **kwargs):
        """
                在我的单据菜单，获取单据状态为[boeStatus]的boeNo_list审批信息，一次最多一百条.data得改
                :param cookie:
                :return:
                """
        api = "/sys/boe/boeList/myDocuments/page"
        headers = {
            "Content-Type": "application/x-www-form-urlencoded",
            "Cookie": cookie
        }
        amountStart = kwargs.get("amountStart") if kwargs.get(
            "amountStart") else ''
        amountEnd = kwargs.get("amountEnd") if kwargs.get("amountEnd") else ''
        boeTypeId = kwargs.get("boeTypeId") if kwargs.get("boeTypeId") else ''
        boeTypeNames = kwargs.get("boeTypeNames") if kwargs.get(
            "boeTypeNames") else ''
        boeStatus = kwargs.get("boeStatus") if kwargs.get("boeStatus") else ''
        startFirstBoeDate = kwargs.get("startFirstBoeDate") if kwargs.get(
            "startFirstBoeDate") else '2021-12-16'  # 12-16之前的脏数据太多了，放弃

        data = f"page={page}&limit=100&orderField=&order=&amountStart={amountStart}&amountEnd={amountEnd}&boeTypeId={boeTypeId}&boeTypeNames={boeTypeNames}&boeStatus={boeStatus}&&startFirstBoeDate={startFirstBoeDate}"
        response = None
        try:
            response = requests.post(
                url=base_url + api, headers=headers, data=data)
            assert "success" in response.text
            return response.json()
        except Exception as error:
            print(
                f"【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 获取[财务初审]没审完的boeNo_list审批信息出错"
                f"\n    get_boeNoList_for_fsc接口异常信息：{error}")
            if error == "":
                print(f"get_boeNoList_for_fsc接口接口响应{response.text}")
            return False

    amountStart = kwargs.get("amountStart") if kwargs.get(
        "amountStart") else ''
    amountEnd = kwargs.get("amountEnd") if kwargs.get("amountEnd") else ''
    boeTypeId = kwargs.get("boeTypeId") if kwargs.get("boeTypeId") else ''
    boeTypeNames = kwargs.get("boeTypeNames") if kwargs.get(
        "boeTypeNames") else ''
    boeStatus = kwargs.get("boeStatus") if kwargs.get("boeStatus") else ''
    data = get_boeNoList_for_fsc(
        cookie,
        amountStart=amountStart,
        amountEnd=amountEnd,
        boeTypeId=boeTypeId,
        boeTypeNames=boeTypeNames,
        boeStatus=boeStatus)  # json
    if data:
        pass
    else:
        return []
    totalCount = GET_VALUE_FROM_JSON_DICT(data, "totalCount")
    boeNo_List = GET_VALUE_FROM_JSON_DICT(data, "boeNo")

    def tipwords(boeStatus, boeNo_List):
        if boeStatus == "25":
            print(
                f"┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┅┄开始财务初审审批{len(boeNo_List)}张单据┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┅┄")
        elif boeStatus == "26":
            print(
                f"┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┅┄开始财务复审审批{len(boeNo_List)}张单据┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┅┄")
        else:
            print(
                f"┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┅┄在我的单据菜单，查到boeStatus={boeStatus}的单据共{len(boeNo_List)}条┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┅┄")

    if totalCount[0] < 100:
        tipwords(boeStatus, boeNo_List)
        return boeNo_List
    else:
        for page in range(2, (totalCount[0] // 100) + 2):
            boeNo_List.extend(
                GET_VALUE_FROM_JSON_DICT(
                    get_boeNoList_for_fsc(
                        cookie,
                        str(page),
                        amountStart=amountStart,
                        amountEnd=amountEnd,
                        boeTypeId=boeTypeId,
                        boeTypeNames=boeTypeNames,
                        boeStatus=boeStatus),
                    "boeNo"))
        # boeNo_List.extend(GET_VALUE_FROM_JSON_DICT(get_boeNoList_for_fsc(cookie, str(page), amountStart='100', amountEnd='100', boeTypeId='d92c9d8c67ebc22176f9b1b9b2ae1308', boeTypeNames='%E5%80%9F%E6%AC%BE%E5%8D%95', boeStatus='25'), "boeNo"))
    tipwords(boeStatus, boeNo_List)
    return boeNo_List


# 在任务调整(主任)菜单，获取任务类型为[taskType]、单据类型为[boeTypeId]、提单人为[employeeId]的boeNo_list信息
def Get_Boe_List_from_fsc(cookie, **kwargs):
    """
        taskType: 001-财务初审；002-财务复审
        :param cookie:
        :param kwargs:
        :return:
        """
    api = "/sys/fsc/fsctaskpools/forDirector/page"
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "Cookie": cookie
    }
    serviceGroupId = kwargs.get("serviceGroupId") if kwargs.get(
        "serviceGroupId") else ''
    boeTypeId = kwargs.get("boeTypeId") if kwargs.get("boeTypeId") else ''
    employeeId = kwargs.get("employeeId") if kwargs.get("employeeId") else ''
    taskType = kwargs.get("taskType") if kwargs.get("taskType") else ''
    taskName = '财务审批'
    if taskType == '001':
        taskName = '财务初审'
    elif taskType == '002':
        taskName = '财务复审'
    data = f'page=1&limit=100&serviceGroupId={serviceGroupId}&boeTypeId={boeTypeId}&employeeId={employeeId}&taskType={taskType}'
    response = None
    try:
        response = requests.post(
            url=base_url + api,
            headers=headers,
            data=data)
        assert "success" in response.text
        data = response.json()
        boeNo_List = GET_VALUE_FROM_JSON_DICT(data, "boeNo")
        if len(boeNo_List) > 0:
            print(
                f"┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┅┄开始{taskName}审批{len(boeNo_List)}张单据┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┅┄")
        else:
            print(
                f"┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┅┄{taskName}审批无任务直接跳过┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┅┄")
        return boeNo_List
    except Exception as error:
        print(
            f"【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 获取[{taskName}]没审完的boeNo_list审批信息出错"
            f"\n    获取财务审批单据列表接口异常信息：{error}")
        if error == "":
            print(f"获取财务审批单据列表接口接口响应{response.text}")
        return False


# 业务审批批量审批流程
def ApprovalBoeList_All(cookie, times):
    """
        业务审批批量审批流程
        :param cookie:
        :param times:
        :param boeHeaderId_list, boeNo_list, taskId_list
        :return:
        """
    # boeNo_list = []
    # boeHeaderId_list = []
    # taskId_list = []
    list_boeNo_now.clear()  # 更新本次单据列表为查到的所有单据

    # 获取boe审批信息
    def get_boeHeaderId_taskId_boeNo(cookie, page='1'):
        """
                获取boeNo_list审批信息，一次最多一百条.data得改
                :param cookie:
                :return:
                """
        api = "/sys/boe/boeList/waitApprove"
        headers = {
            "Content-Type": "application/x-www-form-urlencoded",
            "Cookie": cookie
        }
        data = f"page={page}&limit=100&orderField=&order=&fromAmount=100&toAmount=100&boeNo=&boeTypeId=d92c9d8c67ebc22176f9b1b9b2ae1308&boeTypeNames=%E5%80%9F%E6%AC%BE%E5%8D%95&queryType=20"
        response = None
        try:
            response = requests.post(
                url=base_url + api, headers=headers, data=data)
            assert "success" in response.text
            return response.json()
        except Exception as error:
            print(
                f"【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 获取单据列表信息出错"
                f"\n    get_boeHeaderId_taskId_boeNo接口异常信息：{error}")
            if error == "":
                print(f"get_boeHeaderId_taskId_boeNo接口接口响应{response.text}")
            return False

    # 业务审批批量审批
    def ApprovalBoeList(cookie, boeNo_list, boeHeaderId_list, taskId_list):
        """
                业务审批批量审批
                :param cookie:
                :param boeHeaderId_list, boeNo_list, taskId_list
                :return:
                """
        api = "/sys/boe/core/approvalList"
        headers = {
            "Content-Type": "application/json;charset=UTF-8",
            "Cookie": cookie
        }
        data = []
        response = None

        for index, value in enumerate(boeNo_list):
            boe_body = '{' + \
                f'"boeNo":"{boeNo_list[index]}","boeHeaderId":"{boeHeaderId_list[index]}","taskId":"{taskId_list[index]}","budgetIgnore":"N"' + '}'
            # boe_body = '{"boeNo":{0},"boeHeaderId":{1},"taskId":{2},"budgetIgnore":"N"}'.format(boeNo_list[index], boeHeaderId_list[index], taskId_list[index])
            data.append(boe_body)
        body = '[' + ','.join(data) + ']'
        try:
            response = requests.post(
                url=base_url + api,
                headers=headers,
                data=json.dumps(
                    eval(body)))  # , verify=False
            assert "success" in response.text
            print("成功进行业务审批批量审批    √")
            return response.json()
        except Exception as error:
            print(
                f"【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 业务审批节点出错"
                f"\n          ApprovalBoeList接口异常信息：{error}")
            if error == "":
                print(f"ApprovalBoeList接口接口响应{response.text}")
            # raise error
            return False

    for page in range(1, (times // 100) + 2):  # 分页查询
        boeNo_list_data = get_boeHeaderId_taskId_boeNo(
            cookie, str(page))  # 获得数据json
        boeNo_list = GET_VALUE_FROM_JSON_DICT(boeNo_list_data, "boeNo")
        boeHeaderId_list = GET_VALUE_FROM_JSON_DICT(
            boeNo_list_data, "boeHeaderId")
        taskId_list = GET_VALUE_FROM_JSON_DICT(boeNo_list_data, "taskId")

        if len(boeNo_list) != len(boeHeaderId_list) or len(boeNo_list) != len(
                taskId_list) or len(boeHeaderId_list) != len(taskId_list) or len(boeNo_list) == 0:
            # print("获取boeNo_list_data异常，批量审批失败[1]")
            return False  # 批量审批失败

        # 批量审批
        print(
            f"┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┅┄开始批量业务审批{len(boeNo_list)}张单据┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┅┄")
        ApprovalBoeList(cookie, boeNo_list, boeHeaderId_list, taskId_list)
        list_boeNo_now.extend(boeNo_list)
    return True


# 业务审批-单张
def ApprovalBoe(cookie, boeHeaderId, boeNo, taskId):
    """
        业务审批-单张
        :param cookie:
        :param taskId:
        :return:
        """
    api = "/sys/boe/core/submitApprovalBoe"
    headers = {
        "Content-Type": "application/json;charset=UTF-8",
        "Cookie": cookie
    }
    data = {
        "boeHeaderId": boeHeaderId,
        "boeNo": boeNo,
        "budgetIgnore": "N",
        "boeWorkflowApprovalDto": {
            "taskId": taskId,
            "taskType": "000",
            "boeHeaderId": boeHeaderId,
            "boeNo": boeNo,
            "action": "101"
        }
    }
    response = None
    try:
        response = requests.post(
            url=base_url + api,
            headers=headers,
            data=json.dumps(data))
        assert "success" in response.text
        return response.json()
    except Exception as error:
        print(
            f"【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 业务审批节点出错"
            f"\n    ApprovalBoe接口异常信息：{error}")
        if error == "":
            print(f"ApprovalBoe接口接口响应{response.text}")
        return False


# 流程节点跳过-业务审批节点跳过
def jump(cookie, taskId, boeNo=''):
    """
        流程监控将单据跳转至共享审批节点  # nodeName得更新
        :param cookie:
        :param taskId:
        :return:
        """
    api = "/sys/fmc/monitor/jump"
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "Cookie": cookie
    }
    data = {
        "nodeName": nodeName,  # 得改flag
        "taskId": taskId
    }
    response = None
    try:
        response = requests.post(
            url=base_url + api,
            headers=headers,
            data=data,
            verify=False)
        assert "success" in response.text
        if boeNo:
            boe_jump_ths_queue.put(boeNo)
        return response.json()
    except Exception as error:
        print("")
        print(f"单据:{boeNo}流程监控跳转出错       <----\n", end='')
        if isinstance(error, AssertionError):
            print(f"'流程监控-节点跳过'接口响应:{response.text}\n", end='')
        else:
            print(f"'流程监控-节点跳过'接口异常信息：{str(error)}\n", end='')
        print("")
        return False


def getTaskListByBusinessId(cookie, businessId):
    """
    :param cookie:
    :param businessId:
    :return:
    """
    api = "/fmc/monitor/getTaskListByBusinessId"
    headers = {
        # "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryDW3qzvZhzKyHni4K",
        "Cookie": cookie
    }
    data = {'businessId': businessId,
            }
    encodeData = encode_multipart_formdata(data)
    headers["Content-Type"] = encodeData[1]
    data = encodeData[0]
    response = None
    times = 0
    while True:
        try:
            response = requests.post(
                url=base_url + api,
                headers=headers,
                data=data,
                verify=False)
            assert "success" in response.text
            taskId_list = GET_VALUE_FROM_JSON_DICT(response.json(), 'id')
            if len(taskId_list):
                return taskId_list[0]
            else:
                return False
        except Exception as error:
            times += 1
            if times > 2:
                print("")
                if isinstance(error, AssertionError):
                    print(f"'流程监控-单据完成'接口响应:{response.text}\n", end='')
                else:
                    print(f"'流程监控-单据完成'接口异常信息：{str(error)}\n", end='')
                return False


# 获取初审状态的businessId列表以获取taskId列表
def GetTaskList(cookie, boeNo, boeStatus="", limit="2000"):
    """
    :param cookie:
    :return:
    """
    api = "/sys/fmc/monitor/page"
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "Cookie": cookie
    }
    data = {
        "page": "1",
        "limit": limit,
        "orderField": "",
        "order": "",
        "boeCode": boeNo,
        "employeeName": "",
        "boeStatus": boeStatus,
        "boeTypeName": "",
    }
    response = None
    try:
        response = requests.post(
            url=base_url + api,
            headers=headers,
            data=data,
            verify=False)
        assert "success" in response.text
        businessId_list = GET_VALUE_FROM_JSON_DICT(response.json(), "id")
        if len(businessId_list):
            taskId_list = []
            for businessId in businessId_list:
                taskId = getTaskListByBusinessId(cookie, businessId)
                if taskId:
                    taskId_list.append(taskId)
                else:
                    print(f"taskId:{taskId},【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 单据：{boeNo}在'流程监控-单据完成'接口出错\n", end='')
                    print('')
            return taskId_list
        else:
            return False
    except Exception as error:
        if isinstance(error, AssertionError):
            print(f"'流程监控-获取taskId'接口响应:{response.text}\n", end='')
        else:
            print(f"'流程监控-获取taskId'接口异常信息：{str(error)}\n", end='')
        print('')
        return False


# 跳过共享审批，直接完成单据
def jump_to_complete(cookie, taskId, boeNo_this=None):
    """
        流程监控将单据'完成'
        :param cookie:
        :param taskId:
        :param boeNo_this: 设置非必填参数，满足多线程时使用
        :return:
        """
    api = "/sys/fmc/monitor/complete"
    headers = {
        # "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryDW3qzvZhzKyHni4K",
        "Cookie": cookie
    }
    data = {'taskId': taskId,
            'sequenceFlowName': "",
            'variables': ""
            }
    encodeData = encode_multipart_formdata(data)
    headers["Content-Type"] = encodeData[1]
    data = encodeData[0]
    response = None
    times = 0
    while True:
        try:
            response = requests.post(
                url=base_url + api,
                headers=headers,
                data=data,
                verify=False)
            if not "无效的操作请求！" in response.text:
                assert "success" in response.text
            if boeNo_this:  # 成功
                print(f"单据:{boeNo_this}已完成，将进支付\n", end='')
                list_boeNo.append(boeNo_this)
                success_boeNo.append(boeNo_this)
                list_boeNo_now.append(boeNo_this)
            return response.json()
        except Exception as error:
            times += 1
            time.sleep(10)
            if times > 2:
                print(f"\n单据:{boeNo_this}竟失败，将进遗留       <----\n", end='')
                if isinstance(error, AssertionError):
                    print(f"'流程监控-单据完成'接口响应:{response.text}\n", end='')
                else:
                    print(f"'流程监控-单据完成'接口异常信息：{str(error)}\n", end='')
                print('')
                return False


# 多线程提单(多线程版本)
def submit_boe_ths(cookie, boe_body, goal_boe, times: int):
    """
    提单完成
    :param cookie:
    :param num: 最大线程数
    :return:
    """

    def print_result(future):
        global submit_num_ths
        if future.result():
            submit_num_ths += 1
        # 单个动作后置

    with ThreadPoolExecutor(max_workers=MaxWorkers) as pool:
        for i in range(times):  # 多线程提单times张
            future1 = pool.submit(submit_2, cookie, boe_body, goal_boe)
            future1.add_done_callback(print_result)

    if destination in destination_list[1:]:  # 下一步执行，才塞结束标记，不执行，就等执行那次再塞
        q.put({"flag": True})
    else:
        split_line("执行完成")
        sg.popup_quick_message(" 执行完成! ", auto_close_duration=7, background_color='green')


def jump_ths(cookie):
    """单据列表尽数跳转至共享节点"""

    # jump_num_ths = 0  # 记录跳转执行次数
    # jump_success_ths = 0  # 记录跳转成功执行次数

    def print_result(future):
        global jump_num_ths, jump_success_ths
        jump_num_ths += 1
        if future.result():
            jump_success_ths += 1
    # ThreadPoolExecutor适用于处理I/O 密集型任务。对于CPU密集型任务还不如单线程快。
    with ThreadPoolExecutor(max_workers=MaxWorkers) as pool:
        input_flag = False  # 真完结标志
        while True:  # 提单成功列表尽数跳转
            try:
                dataDict = q.get(timeout=120)
                while dataDict.get("flag"):
                    if jump_num_ths == submit_num_ths:
                        if destination == destination_list[2]:  # 下一步执行，才塞结束标记，不执行，就等执行那次再塞
                            boe_jump_ths_queue.put(True)
                        input_flag = True
                        break
                    time.sleep(2)
                if input_flag:
                    break
                boeNo = dataDict.get("boeNo")
                taskId = dataDict.get("taskId")
                future1 = pool.submit(jump, cookie, taskId, boeNo)
                future1.add_done_callback(print_result)
            except BaseException:
                print("jump_ths方法出现一次error\n", end='')
                # raise error
    if not destination == destination_list[2]:
        print("\n提单成功的单据已尽数进入配置的节点，默认是[共享中心]，请前往[我的单据]菜单检查。\n", end='')
        split_line("执行完成")


# 跳过共享审批，直接完成单据(多线程版本)
def jump_to_complete_ths(cookie):
    """
    流程监控将进共享节点的单据列表尽数'完成'
    :param cookie:
    :param num: 每个线程要完成的审批数
    :return:
    """

    def print_result(future):
        # 单个动作后置
        pass

    with ThreadPoolExecutor(max_workers=MaxWorkers) as pool:
        while True:
            try:
                boeNo_this = boe_jump_ths_queue.get(timeout=300)
                if boeNo_this is True:
                    break
                taskId = GetTaskList(cookie, boeNo_this)
                if taskId:
                    pool.submit(
                        jump_to_complete,
                        cookie,
                        taskId[0],
                        boeNo_this)

            except Exception as error:
                print("jump_to_complete_ths方法出现一次error")
                # raise error
    print("\n执行结果：")
    print(f"本次执行成功生成【{len(success_boeNo)}】张单据,点击[查看]可获取所有执行成功的单据编号；")

    if len(success_boeNo):
        split_line("执行完成")
    else:
        print("请检查配置文件中的nodename字段，配置成单据对应流程中的共享节点的中文名称，默认是[共享中心审批]\n")
        split_line("失败中止")

# 多线程获取支付指令


def Get_pageEfsAndHisEfs_THS(cookie, list_boeNo):
    """多线程获取支付指令"""
    global success_boe_pay
    if len(success_boe_pay) == len(list_boeNo):
        print(
            f"\n成功生成{len(success_boe_pay)}张单据，[单据号+支付指令]如下：\n{success_boe_pay}\n")
        split_line("获取结束")
        return
    else:
        success_boe_pay = []
    global MaxWorkers
    with ThreadPoolExecutor(max_workers=MaxWorkers) as pool:
        for boeNo_this in list_boeNo:
            paymentNo = pool.submit(
                pageEfsAndHisEfs, cookie, boeNo_this).result()
            if paymentNo is False:
                break
            list_tmp = [boeNo_this, paymentNo]
            success_boe_pay.append(list_tmp)
            # pageEfsAndHisEfs(cookie, boeNo_this)

    print(
        f"\n成功生成{len(success_boe_pay)}张单据，[单据号+支付指令]如下：\n{success_boe_pay}\n")
    split_line("获取结束")


def run_timer(cookie, taskType):
    """
        运行定时器
        :param taskType:
        :return:
        """
    api = "/sys/job/schedule/run"
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "Cookie": cookie
    }
    data = {
        "jobId": "GP00fsc001" if taskType == "001" else "GP00fsc002"
    }

    try:
        response = requests.put(
            url=base_url + api,
            headers=headers,
            data=data,
            verify=False)
        assert ("已启动" in response.text) or ("当前任务正在运行中" in response.text)
        print(f"{'从中台同步单据至共享' if taskType == '001' else '生成任务'}定时器成功执行")
        return True
    except Exception as error:
        print(
            f"【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 运行{'从中台同步单据' if taskType == '001' else '生成任务'} 定时器出错。\n"
            f"    异常信息：{error}")
        return False


# 任务调整(主任)-查询接口获取单据taskId
def _adjustTaskStatusByBoeNo(cookie, boeNo):
    """
        任务调整(主任)-查询接口获取单据taskId
        :param boeNo:
        :return:taskId OR False
        """
    # api = "/sys/fsc/fsctaskpools/adjustTaskStatusByBoeNo"
    api = "/sys/fsc/fsctaskpools/forDirector/page"  # 任务调整(主任)-查询
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "Cookie": cookie
    }
    data = {
        "boeNo": boeNo
    }
    response = requests.post(
        url=base_url + api,
        headers=headers,
        data=data,
        verify=False)
    try:
        assert "success" in response.text
        r = response.json().get("page")
        if r.get("totalCount") == 1:
            if isinstance(r.get("list"), list):
                taskId = r.get("list")[0]["id"]
            else:
                print("单据", boeNo, ":分配共享审核人员失败，正在重试")
                return False
        else:
            print("单据", boeNo, ":分配共享审核人员失败，正在重试")
            return False
    except Exception as e:
        print(f"【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 获取单据fscTaskId出错，接口响应：{response.text}\n")
        return False
    # raise e
    # return response.json()
    return taskId


# 任务调整(主任)-任务收回接口将单据收回到未分配状态
def _fsc_recoverByDirector(cookie, taskId):
    """
        # 任务调整(主任)-任务收回接口将单据收回到未分配状态
        :param taskId: _adjustTaskStatusByBoeNo接口查出来的单据的id
        :return:
        """
    api = "/sys/fsc/fsctaskpools/recoverByDirector"  # 任务调整(主任)-任务收回
    headers = {
        "Content-Type": "application/json;charset=UTF-8",
        "Cookie": cookie
    }
    data = [
        taskId
    ]
    # data.append(taskId)
    response = requests.put(
        url=base_url + api,
        headers=headers,
        data=json.dumps(data))
    try:
        if "任务未分配到组" in response.text:
            print("成功将单据收回到未分配状态    √")
            return False
        assert "success" in response.text
        print("成功将单据收回到未分配状态    √")
        return False
    except Exception as e:
        print(
            f"【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 任务调整(主任)-任务收回接口将单据收回到未分配状态出错\n    "
            f"接口响应：{response.text}")
        return True
    # raise e


# 任务调整(主任)-分配到组接口，根据任务id和组id将任务分配到指定组
def _assignToGroup(cookie, taskId, fGroupId):
    """
         任务调整(主任)-分配到组接口，根据任务id和组id将任务分配到指定组
        :param
        :param taskId:taskId
        :param fGroupId:组id
        :return:无
        """
    # 中旅211环境的“共享初审组”
    # fGroupId=cc9866de-dd3b-4004-99d2-efe06d045ef5
    # http://10.88.4.211/sys/fsc/fsctaskpools/assignToGroup
    api = "/sys/fsc/fsctaskpools/assignToGroup"  # 任务调整(主任)-分配到组
    headers = {
        "Cookie": cookie,
        "Content-Type": "application/json;charset=UTF-8"
    }
    data = {
        "ids": [
            taskId
        ],
        "fGroupId": fGroupId,
        "reason": "",
        "priorityLevel": 0
    }
    resp = requests.put(
        url=base_url + api,
        headers=headers,
        data=json.dumps(data))
    try:
        assert "success" in resp.text
        print("成功分配到指定组    √")
        return False
    except Exception as e:
        print("分配到指定组失败，请检查配置文件的初审组id(fgroupid)配置是否正确> > >", resp.json())
        return True


# 任务调整(主任)-分配到组接口，根据任务id和审核人id将任务分配到指定人
def _assignToPeople(cookie, taskId, operatorUserId):
    """
         任务调整(主任)-分配到组接口，根据任务id和审核人id将任务分配到指定人
        :param
        :param taskId:taskId
        :param operatorUserId:审核人ID
        :return:无
        """
    # 中旅211环境的“共享初审组”的SIT用户08
    # operatorUserId=3725d3a2-90c2-47bd-bd4a-d6917f2f729a
    # http://10.88.4.211/sys/fsc/fsctaskpools/assignToPeopleByParam
    api = "/sys/fsc/fsctaskpools/assignToPeopleByParam"  # 任务调整(主任)-分配到人
    headers = {
        "Cookie": cookie,
        "Content-Type": "application/json"
    }
    data = {
        "ids": [
            taskId
        ],
        "operatorUserId": operatorUserId,
        "reason": "",
        "priorityLevel": 0
    }
    resp = requests.put(
        url=base_url + api,
        headers=headers,
        data=json.dumps(data))
    try:
        assert "success" in resp.text
        print("成功分配到指定人    √")
        return False
    except Exception as e:
        print("分配到指定人失败，请检查配置文件的初审人id(operatoruserid)配置是否正确> > >", resp.json())
        return True


# 将单据分配到某组某人的”我的工作台菜单“
def assignToPeople(cookie, taskId, fGroupId, operatorUserId):
    """ 将单据分配到某组某人的”我的工作台菜单“ """
    # 中旅211环境的“共享初审组”
    # fGroupId=cc9866de-dd3b-4004-99d2-efe06d045ef5
    # 中旅211环境的“共享初审组”的SIT用户08
    # operatorUserId=3725d3a2-90c2-47bd-bd4a-d6917f2f729a
    s = time.time()
    flag = False
    while True:
        if taskId:
            # 将单据任务收回
            if _fsc_recoverByDirector(cookie, taskId):
                continue
            # 重新分配到组
            if _assignToGroup(cookie, taskId, fGroupId):
                continue
            # 重新分配到人
            if _assignToPeople(cookie, taskId, operatorUserId):
                continue
            flag = True
            break
        # 处理定时器延迟导致单据没进共享的问题，给点缓冲 30s
        elif time.time() - s > 30:
            break
        else:
            time.sleep(random.random())
    if flag:
        print(f"成功分配到“我的工作台”菜单    √")
    else:
        return False


# 按单据号获取任务
def extractTaskByBoeNo(cookie, taskId):
    """
        按单据号获取任务
        http://base_url/sys/fsc/fsctaskpools/extractTaskByBoeNo
        :return:
        """

    def test(cookie, taskId):
        api = "/sys/fsc/fsctaskpools/assignToPeopleByBoeNo"  # 按单据号获取任务
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        data = {"ids": [taskId]}
        resp = requests.put(
            url=base_url + api,
            headers=headers,
            data=json.dumps(data))
        try:
            assert "success" in resp.text
            print("成功按单据号获取任务    √")
            return True
        except Exception as e:
            traceback.print_exc()
            return False

    s = time.time()
    while True:
        if test(cookie, taskId):
            return True
        if time.time() - s > 30:  # 尝试30秒
            print(f"按单据号获取任务失败，将放弃{taskId}")
            return False
        print("按单据号获取任务失败，将再次进行尝试...")


# 共享中心-我的工作台菜单获取boeId
def get_boeId(cookie, boeNo):
    """
        获取boeId。  data得改   # 得改flag
        """
    api = "/sys/fsc/fsctaskpools/forLader/page"
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "Cookie": cookie
    }
    data = f"page=1&limit=20&orderField=&order=&serviceGroupId=3d3d9c111e8720af4baeb1b2b7ae0009&serviceGroupName=%E5%B9%BF%E5%B7%9E%E5%86%9C%E6%9D%91%E5%95%86%E4%B8%9A%E9%93%B6%E8%A1%8C%E8%82%A1%E4%BB%BD%E6%9C%89%E9%99%90%E5%85%AC%E5%8F%B8&leId=&leName=&boeTypeId=&boeTypeName=&employeeName=&employeeId=&boeNo={boeNo}"
    response = None
    try:
        response = requests.post(
            url=base_url + api,
            headers=headers,
            data=data)
        assert "success" in response.text
        return response.json()
    except Exception as error:
        print(f"【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 更新单据细节出错：\n    接口响应：{response.text}\n{error}")
        return False


# 审批节点全部通过前先更新单据细节
def updateDetailAndDelete(cookie, boeNo):
    """
        审批节点全部通过后更新单据细节 # 借款单
        :param cookie:
        :param taskId: 单据id
        :param boeNo: 传进来仅记录
        :return:
        """
    boeId_tmp = get_boeId(cookie, boeNo)
    if boeId_tmp is False:
        return False
    boeId_tmp = GET_VALUE_FROM_JSON_DICT(boeId_tmp, "boeId")
    if len(boeId_tmp) == 0:
        return False
    boeId = boeId_tmp[0]
    api = "/sys/fsc/fscvoucherheaders/updateDetailAndDelete"
    headers = {
        "Content-Type": "application/json;charset=UTF-8",
        "Cookie": cookie
    }
    data = {
        "fscVoucherHeaders": [],
        "warnCodes": "",
        "boeId": boeId
    }
    response = None
    try:
        response = requests.post(
            url=base_url + api,
            headers=headers,
            data=json.dumps(data))
        assert "success" in response.text
    except Exception as error:
        print(f"【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 更新单据细节出错：\n    接口响应：{response.text}\n{error}")
        return False


# 单据共享审批
def _fsc_approval(cookie, taskId, boeNo=''):
    """
        单据共享审批
        :param cookie:
        :param taskId: 单据id
        :param boeNo: 传进来仅记录
        :return:
        """
    api = "/sys/fsc/fsctaskpools/approval"
    headers = {
        "Content-Type": "application/json;charset=UTF-8",
        "Cookie": cookie
    }
    data = {
        "taskId": taskId,
        "result": True,
        "currentFlag": "1",
        "rejectReasonType": "",
        "approvalComment": "",
        "scores": {
            "score": {},
            "adjustReason": ""
        }
    }
    response = "接口无响应"
    try:
        response = requests.put(
            url=base_url + api,
            headers=headers,
            data=json.dumps(data))
        if str(response.json()["code"]) == "12089":  # msg=影像状态不达标，是否审核通过
            print("> > >共享审批warnCodes：12089")
            data = {
                "taskId": taskId,
                "result": True,
                "currentFlag": "1",
                "rejectReasonType": "",
                "approvalComment": "",
                "scores": {
                    "score": {},
                    "adjustReason": ""
                },
                "warnCodes": "12089,budgetIgnore"
            }
            response = requests.put(
                url=base_url + api,
                headers=headers,
                data=json.dumps(data))
        assert "success" in response.text
    except Exception as error:
        print(
            f"【{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}】 共享审批同意出错：\n    单号：{boeNo}\n    接口响应：{response.text}")
        return False
    # raise error
    success_boeNo.append(boeNo)
    print(f"{boeNo} 共享审批完成")
    return response.json()


# 共享初审全流程组装函数
def Approval_Process(
        cookie,
        boeNo,
        getbyboe=True,
        fGroupId='cc9866de-dd3b-4004-99d2-efe06d045ef5',
        operatorUserId='3725d3a2-90c2-47bd-bd4a-d6917f2f729a'):
    """
        共享审批全流程
        :param cookie: 带有主任菜单权限的共享审批人的cookie
        :param boeNo: 单据编号
        :param fGroupId: 共享初审组id
        :param operatorUserId: 财务初审审批人id
        :return:
        """
    # 中旅211环境的“共享初审组”
    # fGroupId=cc9866de-dd3b-4004-99d2-efe06d045ef5
    # 中旅211环境的“共享初审组”的SIT用户08
    # operatorUserId=3725d3a2-90c2-47bd-bd4a-d6917f2f729a

    get_times = 0
    while True:
        get_times = get_times + 1
        taskId = _adjustTaskStatusByBoeNo(cookie, boeNo)
        if get_times > 3:  # 尝试十次 要改flag
            return False
        if taskId:  # taskId获取到了
            # print(f"------------------开始财务初审：{boeNo}")
            break
        else:  # 没获取到
            # 等待执行定时器同步
            time.sleep(10)
            continue
    if getbyboe:
        # 将单据任务收回
        _fsc_recoverByDirector(cookie, taskId)
        # 重新分配到组
        _assignToGroup(cookie, taskId, fGroupId)
        # 直接按单据号获取任务
        extractTaskByBoeNo(cookie, taskId)
    else:
        # 将单据分配到某组某人的”我的工作台菜单“
        assignToPeople(cookie, taskId, fGroupId, operatorUserId)
    # 审批节点全部通过前先更新单据细节
    updateDetailAndDelete(cookie, boeNo)
    # 单据共享审批通过
    if _fsc_approval(cookie, taskId, boeNo) is False:
        return False
    else:
        return True


# 共享复审全流程组装函数
def Approval_Process_Again(
        cookie,
        boeNo,
        getbyboe,
        fGroupId='cc9866de-dd3b-4004-99d2-efe06d045ef5',
        operatorUserId='3725d3a2-90c2-47bd-bd4a-d6917f2f729a'):
    """
        共享审批全流程
        :param cookie: 带有主任菜单权限的共享审批人的cookie
        :param boeNo: 单据编号
        :param fGroupId: 共享复审组id
        :param operatorUserId: 财务复审审批人id
        :return:
        """
    # 中旅211环境的“共享初审组”
    # fGroupId=cc9866de-dd3b-4004-99d2-efe06d045ef5
    # 中旅211环境的“共享初审组”的SIT用户08
    # operatorUserId=3725d3a2-90c2-47bd-bd4a-d6917f2f729a

    # 判断单据状态
    # pass
    # print(f"单号：{boeNo} 开始共享审批流程")
    get_times = 0
    while True:
        get_times = get_times + 1
        taskId = _adjustTaskStatusByBoeNo(cookie, boeNo)
        if get_times > 10:  # 尝试十次
            return False
        if taskId:  # 获取到了
            # print(f"------------------开始财务复审：{boeNo}")
            break
        else:  # 没获取到
            # 等待执行定时器同步
            time.sleep(10)
            continue
    if getbyboe:
        # 将单据任务收回
        _fsc_recoverByDirector(cookie, taskId)
        # 重新分配到组
        _assignToGroup(cookie, taskId, fGroupId)
        # 直接按单据号获取任务
        extractTaskByBoeNo(cookie, taskId)
    else:
        # 将单据分配到某组某人的”我的工作台菜单“
        assignToPeople(cookie, taskId, fGroupId, operatorUserId)
    # 审批节点全部通过前先更新单据细节
    updateDetailAndDelete(cookie, boeNo)
    # 单据共享审批通过
    if _fsc_approval(cookie, taskId, boeNo) is False:
        return False
    else:
        return True


# 获取支付中心单据对应的id
def pageAuditingPrepay(cookie, boeNo):
    """获取支付中心单据对应的id"""

    api = "/sys/op/opprepaypayment/pageAuditingPrepay"
    headers = {
        "Cookie": cookie,
        "Content-Type": "application/x-www-form-urlencoded"
    }
    data = f"page=1&limit=20&orderField=&order=&fsscGroupId=&groupId_EQ=&groupName=&boeTypeId_EQ=&billTypeName=&leId_EQ=&leName=&vendorId_EQ=&vendorName=&employeeId_EQ=&employName=&paymentStatus_EQ=&paymentModeCode_EQ=&paymentModeName=&boeNo_LK={boeNo}"
    try:
        resp = requests.post(url=base_url + api, headers=headers, data=data)
        assert "success" in resp.text
        PrepayId = resp.json().get("page")["list"][0]["id"]
        return PrepayId
    except Exception as e:
        # print("获取支付中心单据对应的id接口异常:", e)
        return False


# 出纳审核菜单批量审核
def cashierAuditBatch(cookie, boeNo):
    """出纳审核菜单批量审核"""
    PrepayId_list = []
    PrepayId = pageAuditingPrepay(cookie, boeNo)
    if PrepayId is not False:
        PrepayId_list.append(PrepayId)
    else:
        # print(f"单据：{boeNo}无需出纳审核\n", end='')
        return True

    api = "/sys/op/opprepaypayment/cashierAuditBatch"
    headers = {
        "Cookie": cookie,
        "Content-Type": "application/json;charset=UTF-8"
    }
    times = 0
    while True:
        try:
            resp = requests.post(
                url=base_url + api,
                headers=headers,
                json=PrepayId_list)
            assert "success" in resp.text, print(
                f"出纳审核菜单批量审核接口断言失败，接口响应：{resp.text}\n", end='')
            print(f"单据：{boeNo}完成出纳审核\n", end='')
            return True
        except Exception as e:
            traceback.print_exc()
            times += 1
            if times > 2:
                print(f"出纳审核菜单批量审核接口异常:{e}\n", end='')
                return False


# 出纳审核
def pageEfsAndHisEfs(cookie, boeNo):
    """
         支付中心-支付查询接口，根据boeNo查询支付指令paymentNo
        :param
        :param taskId:taskId
        :param fGroupId:组id
        :return:无
        """
    # 出纳审核
    cashierAuditBatch(cookie, boeNo)

    api = "/sys/op/opefspayment/pageEfsAndHisEfs"
    headers = {
        "Cookie": cookie,
        "Content-Type": "application/x-www-form-urlencoded"
    }
    data = {
        "boeNo_LK": boeNo
    }
    try:
        resp = requests.post(url=base_url + api, headers=headers, data=data)
        assert "success" in resp.text, print(
            f"查询支付指令接口断言失败，接口响应：{resp.text}\n", end='')
        if resp.json().get("page")["totalCount"]:
            paymentNo = resp.json().get("page")["list"][0]["paymentNo"]
            return paymentNo
        return "null"
    except Exception as e:
        print(f"查询支付指令接口异常:{e}\n", end='')
        return False


def split_line(print_sql):
    split_line = "-" * 35
    print(
        f'\n{split_line}   {print_sql}[{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}]  {split_line}\n',
        end='')


def json_java_py(jsonText: str):
    # 请求体json,JAVA转PYTHON
    if not isinstance(jsonText, str):
        print("jsonText请传json的str类型")
        return False
    return jsonText.replace('true', 'True').replace('false', 'False').replace('null', 'None')


def cut_windows(value, windows):
    """切换界面方法"""
    bill_type = value['bill_type']
    bill_type_str = bill_type.split('_')[0]
    if bill_type not in list_bill_type:
        sg.popup("该[场景]暂不支持\n请重选", title='提示')
        return

    windows.close()
    if bill_type_str == "1":
        windowSubmitBoe()
    if bill_type_str == "2":
        windowDataTools()
    if bill_type_str == "3":
        windowFormatParams()
    # if bill_type_str == "4":
    #     windowDataTools()


def event_base_config(event, value, windows, a2):
    # 右键菜单方法
    # return break 时break
    global list_boeNo, fial_boeNo, success_boeNo, success_boe_pay
    global path_pic

    if event == "源码":
        if sg.popup_yes_no("是否打开存放源码的博客?", title='确认') == 'Yes':
            url = 'https://blog.csdn.net/cat_l_over/article/details/122827795'
            webbrowser.open(url)
    elif event in ["bill_type", "base_url"] or event == "刷新":
        if event in ["bill_type", "base_url"]:
            save_data(value, a2, flag_UpdateConf)
            if value.get('base_url'):
                choice_env(flag="write", content=value.get('base_url'))
        if event == "base_url":
            if list_boeNo != []:
                if sg.popup_yes_no('检测到环境切换\n是否清空之前的单据记录?', title='确认') == 'Yes':
                    list_boeNo = []  # wds定义记录全部生成的单据
                    fial_boeNo = []  # wds定义记录全部失败的单据
                    success_boeNo = []  # wds定义记录全部成功共享审批的单据
                    success_boe_pay = []  # wds定义记录全部成功共享审批的单据+支付指令
        cut_windows(value, windows)

    elif event == "清空":
        # 点击清空按钮
        # 1、清空可能会变化的字段信息（没做）
        # 2、清空日志栏
        # 3、清空历史数据记录
        global list_base_num, list_base_num_more
        windows['debug_result'].Update(value='')
        list_base_num = [0] * len(list_base)  # 数据工厂数量记录
        list_base_num_more = [0] * len(list_base_more)
        if list_boeNo != []:
            if sg.popup_yes_no('是否清空之前的单据记录?', title='确认') == 'Yes':
                list_boeNo = []  # wds定义记录全部生成的单据
                fial_boeNo = []  # wds定义记录全部失败的单据
                success_boeNo = []  # wds定义记录全部成功共享审批的单据
                success_boe_pay = []  # wds定义记录全部成功共享审批的单据+支付指令
    elif event == "配置":
        sg.popup_quick_message("配置文件修改保存后\n需右键[刷新]才可生效!", auto_close_duration=7, background_color='#0099ff')
        # 打开配置文件路径
        test_env = value.get('base_url')
        path = path_pic + rf'CHN_TGtools({test_env.split("//")[-1]}).ini'  # 配置文件路径
        if not os.path.exists(path):  # 判断文件是否存在
            if not os.path.exists(path_pic):  # 判断文件夹是否存在
                os.makedirs(path_pic)
            tapdconf_reset(path)  # 重置配置文件
        os.startfile(path)


class ZTCCLOUD(object):

    @staticmethod
    def GET_DICT(dict1, values, **kwargs):
        """
        从响应中根据key获取对应的值(第一个)
        :param dict1: 响应结果json
        :param values: key
        :param **kwargs: 增加筛选条件，字典中满足kwarg传入的‘查询条件key’=‘value’时，才取‘目标key’对应的值，否则忽略
        :return: 查到的值(str)
        """
        value = ""
        values1 = values

        def GET_LIST(list1, values1):
            for i in list1:

                if isinstance(i, dict):
                    global value
                    value = ZTCCLOUD.GET_DICT(i, values1, **kwargs)
                elif isinstance(i, list):
                    value = GET_LIST(i, values1)
                else:
                    continue
                if value:
                    return value

        for k, v in dict1.items():
            if value:
                break
            flag = True
            for kwargsK, kwargsV in kwargs.items():
                if dict1.get(kwargsK) != kwargsV:
                    flag = False
            if k == values:
                if flag:
                    value = v
                    return value
            elif isinstance(v, dict):
                value = ZTCCLOUD.GET_DICT(v, values1, **kwargs)
            elif isinstance(v, list):  # 判断类型是不是list
                value = GET_LIST(v, values1)
            else:
                continue
            if value:
                return value

        return value

    @staticmethod
    def GET_VALUE_FROM_JSON_DICT(_obj, key, **kwargs):
        """
        从响应中根据key获取值(所有)
        :param _obj: 响应结果json
        :param key: 目标key
        :param **kwargs: 增加筛选条件，字典中满足kwarg传入的‘查询条件key’=‘value’时，才取‘目标key’对应的值，否则忽略
        :return: 查到的值(list)
        """
        ret = []
        # print(f"----------->过滤查询条件：{kwargs}")
        def _get_value_from_json_dict(_obj, _key, **kwargs):
            if isinstance(_obj, list):
                for _i in _obj:
                    _get_value_from_json_dict(_i, _key, **kwargs)
            elif isinstance(_obj, dict):
                flag = True
                for kwargsK, kwargsV in kwargs.items():
                    if _obj.get(kwargsK) != kwargsV:
                        flag = False
                for _k, _v in _obj.items():
                    if _k == _key:
                        if flag:
                            ret.append(_v)
                    else:
                        _get_value_from_json_dict(_v, _key, **kwargs)
            else:
                return

        _get_value_from_json_dict(_obj, key, **kwargs)

        return ret

    @staticmethod
    def random_character(char_type="int", length="4"):
        """
        根据字符类型和长度生成随机字符
        :param char_type: 字符类型(int: 数字；str：大小写英文字母；all：数字+字母+符号)，默认值为int
        :param length: 所需生成的字符长度，默认值为4
        :return:
        """
        try:
            length = int(length)
        except ValueError as e:
            raise ValueError("----------> 长度(length字段)只能输入数字，请重新输入")
        if char_type.lower() == "int":
            s = string.digits
        elif char_type.lower() == "str":
            s = string.ascii_letters
        elif char_type.lower() == "all":
            s = string.digits + string.ascii_letters + string.punctuation
        else:
            raise ValueError("----------> 当前只支持 int、str、all 三种选项，请重新选择")
        random.shuffle(list(s))
        return "".join(random.choice(s) for _ in range(length))

    def FINDDATA(self, serverIp, cookie, data, url="/sys/base/lov/findDatas"):
        """获取LOV数据。data：请求体数据"""
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        try:
            data = eval(data)
        except:
            pass
        # print(f"---------->请求体：\n{data}\n")
        response = requests.post(timeout=timeout, url=request_url, headers=headers, data=data.encode('utf-8'))
        # print(f"----------> 获取LOV数据  接口响应：{response.text}")
        assert response.status_code == 200
        return response.json()

    def GET_USER_COOKIE(
            self,
            serverIp,
            user_name,
            password,
            login_url="/sys/auth/login",
            *args,
            **kwargs):
        """
        共享系统获取cookie
        :param serverIp:
        :param user_name:
        :param password:
        :param protocol:
        :param port:
        :param login_url:
        :param args:
        :param kwargs:
        :return:
        """
        request_url = serverIp + login_url
        headers = {
            "Content-Type": "application/x-www-form-urlencoded"
        }
        data = {
            "loginKey": user_name,
            "password": PrpCrypt().encrypt(password),
            "multiGroup": "true",
            "loginPlatform": "PC"
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data)
        try:
            assert response.status_code == 200
        except BaseException:
            sg.popup_quick_message("登录出现异常，请检查测试环境", title="异常")
            return False
        cookies = response.cookies.get_dict()
        cookie = f"{list(cookies.keys())[0]}={list(cookies.values())[0]}"
        return cookie

    def GET_USER_AUTH(
            self,
            serverIp,
            user_name,
            password,
            login_url="/evssys/init/login",
            *args,
            **kwargs):
        """
        影像、档案系统获取authorization
        :param serverIp:
        :param user_name:
        :param password:
        :param protocol:
        :param port:
        :param login_url:
        :param args:
        :param kwargs:
        :return:
        """
        request_url = serverIp + login_url
        headers = {
            "Content-Type": "application/json"
        }
        data = {
            "username": user_name,
            "password": password,
            "language": "zh_CN"
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=json.dumps(data))
        try:
            assert "true" in response.text
        except BaseException:
            sg.popup_quick_message("登录出现异常，请检查测试环境", title="异常")
            return False
        Authorization = response.json().get("Authorization")
        return Authorization

    def QUERY_DEPT_FINDCHILDRENBYIDANDVALIDITY(
            self,
            baseIp,
            cookie,
            data=None,
            url="/sys/common/dept/findChildrenByIdAndValidity",
            *args,
            **kwargs):
        """
        基础数据-组织数据-公司部门 查询当前集团在部门表中的id
        \n:return id
        """
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json"
        }
        if data:
            kwargs.update(eval(data))
        data = {
            "pid": "0",
            "validityFlag": "0",
            "org": "0^1^2^4"
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=json.dumps(data))
        # print(
        #     f"----------> 基础配置-流程配置-审批角色线 查询当前集团在部门表中的id 接口响应：{response.text}")
        assert response.status_code == 200, "{} 接口响应状态码不是200".format(
            self.__doc__.split("\n")[1].strip("\t"))
        return response.json().get("tree")[0].get("id")

    def QUERY_DEPT_FINDPARENTBYNAMEANDVALIDITY(
            self,
            baseIp,
            cookie,
            code=None,
            name=None,
            data=None,
            url="/sys/common/dept/findParentByNameAndValidity",
            *args,
            **kwargs):
        """
        基础数据-组织数据-公司部门 按名称关键字过滤公司/部门/岗位/人员信息(左侧树)
        \n:param name: 名称关键字
        """
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json"
        }
        if data:
            kwargs.update(eval(data))
        data = {
            "name": name,
            "code": code
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=json.dumps(data))
        # print(
        #     f"----------> 基础配置-流程配置-审批角色线 按名称关键字过滤公司/部门/岗位/人员信息(左侧树) 接口响应：{response.text}")
        assert response.status_code == 200, "{} 接口响应状态码不是200".format(
            self.__doc__.split("\n")[1].strip("\t"))
        return response.json()

    def ADD_COMMON_DEPT(
            self,
            baseIp,
            cookie,
            dType,
            code,
            name_zh_CN,
            deptLevel,
            pid,
            deptFid,
            data=None,
            url="/sys/common/dept",
            *args,
            **kwargs):
        """
        基础数据-组织数据-公司部门 新增公司/部门/岗位/人员
        \n:param dType: 类型  枚举值：{'0': '公司'， ‘1’: '部门', '2': '岗位', '3': '人员', '4': '集团'}
        \n:param code: 编码
        \n:param name_zh_CN: 名称(中)
        \n:param deptLevel: 级别  枚举值：{'LV1': '一级', 'LV2': '二级', 'LV3': '三级', 'LV4': '四级', 'LV5': '五级'}  新建岗位时，传入任意值
        \n:param pid: 父级Id
        \n:param deptFid: 级别顺序Id、类型序列  Id与类型之间用^拼接，多个值之间用/拼接  例如：/集团Id^4/公司Id^0/部门Id^1/岗位Id^2/......

        如果要传入其他参数，请参照下列示例：
        新建公司：
                名称(En)：name_en_US=名称(En)
                负责人：leaderId=负责人Id
                第二负责人：leaderSecId=第二负责人Id
                法人：legalPerson=法人Id

        新建部门：
                名称(En)：name_en_US=名称(En)
                部门负责人：leaderId=部门负责人Id
                部门分管领导：leaderSecId=部门分管领导Id
                法人：legalPerson=法人Id
                部门属性：funArea=部门属性  枚举值：{'DEP4': '管理', 'DEP0': '咨询', 'DET3': '销售', 'DEP1': '研发', 'DEP2': '生产'}

        新建岗位：
                名称(En)：name_en_US=名称(En)
        """
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json"
        }
        if data:
            kwargs.update(eval(data))
        data = {
            # "deptFname": "深圳市中兴新云服务有限公司/ZTESSC",
            "type": dType,
            "code": code,
            "name_zh-CN": name_zh_CN,
            "name_en-US": kwargs.get("name_en_US") or kwargs.get("name_en-US"),
            "leaderId": kwargs.get("leaderId"),
            # "leaderName": "财务审批",
            "leaderSecId": kwargs.get("leaderSecId"),
            # "leaderSecName": "嘤嘤弦",
            "legalPerson": kwargs.get("legalPerson"),
            # "legalPersonName": "申能集团商务服务有限公司",
            "deptLevel": deptLevel,
            "ccName": "",
            "validityFlag": "0",
            "deptFid": deptFid,
            # "deptFno": "ZTESSC",
            "pid": pid,
            "languages": [
                {
                    "language": "zh-CN",
                    "name": name_zh_CN
                },
                {
                    "language": "en-US",
                    "name": kwargs.get("name_en_US") or kwargs.get("name_en-US")
                }
            ]
        }
        if str(dType) == "1":
            data["funArea"] = kwargs.get("funArea")
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=json.dumps(data))
        # print(f"----------> 基础数据-组织数据-公司部门 新增公司/部门/岗位/人员 接口响应：{response.text}")
        assert response.status_code == 200, "{} 接口响应状态码不是200".format(
            self.__doc__.split("\n")[1].strip("\t"))
        return response.json()

    def ADD_COMMON_EMP(
            self,
            baseIp,
            cookie,
            empNo,
            realName_zh_CN,
            gender='0',
            permanentLand=None,
            data=None,
            url="/sys/common/emp",
            *args,
            **kwargs):
        """
        基础数据-组织数据-员工信息 新增员工(必填信息，后期补充)
        \n:param empNo: 员工工号
        \n:param realName_zh_CN: 员工工号
        \n:param gender: 性别  枚举值：{'0': '男', '1': '女'}
        \n:param permanentLand: 常驻地Id  多个值用^连接，例如：id1^id2^...

        如果要传入其他参数，请参照下列示例：
                邮箱：email=邮箱地址
                微信：wxNumber=微信号
                钉钉：ddNumber=钉钉号
                是否生成用户：haveAccount=Y  枚举值：{'Y': '生成用户', 'N': '不生成用户'}
                手机：mobile=手机号
                主岗：mainPost=岗位id
                兼岗：accessoryPost=['id1', 'id2']
        """

        # 查个驻地id
        permanentLand_temp = None
        try:
            if not permanentLand:
                permanentLand_temp = self.QUERY_CITY_INFO(baseIp, cookie)
        except:
            pass
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json"
        }
        if data:
            kwargs.update(eval(data))
        # sg.popup_yes_no(f'{kwargs.get("accessoryPost")},,,{type(kwargs.get("accessoryPost"))}')
        accessoryPost = kwargs.get("accessoryPost") if (kwargs.get("accessoryPost") and isinstance(kwargs.get("accessoryPost"), list)) else []
        data = {
            "empNo": empNo,
            "realName_zh-CN": realName_zh_CN,
            "realName_en-US": "",
            "gender": gender if gender else '0',
            "permanentLand": permanentLand if permanentLand else permanentLand_temp,
            "permanentLandName": "",
            "mainPost": kwargs.get("mainPost"),
            "mainPostName": kwargs.get("mainPostName"),
            "deptFname": "",
            "companyId": kwargs.get("companyId"),
            "companyName": kwargs.get("companyName"),
            "superLeaderId": kwargs.get("superLeaderId"),
            "superLeaderName": kwargs.get("superLeaderName"),
            "position": "",
            "title": "",
            "travelLevel": "",
            "mobile": kwargs.get("mobile"),
            "messageType": [
                # "noticeEmail",
                # "noticeWx",
                # "noticeDd"
            ],
            "email": kwargs.get("email"),
            "wxNumber": kwargs.get("wxNumber"),
            "ddNumber": kwargs.get("ddNumber"),
            "haveAccount": kwargs.get("haveAccount"),
            "USER_ID": "",
            "languages": [
                {
                    "language": "zh-CN",
                    "realName": realName_zh_CN
                },
                {
                    "language": "en-US",
                    "realName": ""
                }
            ],
            "noticeEmail": kwargs.get("noticeEmail") or 1,
            "noticeWx": kwargs.get("noticeWx") or 1,
            "noticeDd": kwargs.get("noticeDd") or 1,
            "vendorsBank": [],
            "accessoryPost": accessoryPost,
            "superLeaderIds": [""] * len(accessoryPost),  # superLeaderIds列表元素数量需与accessoryPost一一对应，无则补""
            "positions": [""] * len(accessoryPost),
            "titles": [""] * len(accessoryPost),
            "travelLevels": [""] * len(accessoryPost)
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=json.dumps(data))
        # print(f"----------> 基础数据-组织数据-员工信息 新增员工 接口响应：{response.text}")

        assert response.status_code == 200, "{} 接口响应状态码不是200".format(
            self.__doc__.split("\n")[1].strip("\t"))
        return response.json()

    def QUERY_CITY_INFO(
            self,
            baseIp,
            cookie,
            data=None,
            url="/sys/base/lov/findDatas",
            *args,
            **kwargs):
        """
        基础数据-组织数据-员工信息-新增 查询常驻地信息
        随即返回一个驻地城市的id

        如果要传入其他参数，请参照下列示例：
                编码：CODE=编码
                城市名称：CITY_NAME=城市名称
        """
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            kwargs.update(eval(data))
        data = {
            "page": kwargs.get("page") or "1",
            "limit": kwargs.get("limit") or "5",
            "orderField": kwargs.get("orderField"),
            "order": kwargs.get("order"),
            "CODE": kwargs.get("CODE"),
            "CITY_NAME": kwargs.get("CITY_NAME"),
            "lovKey": "sysCityMode.findCity"
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data)
        # print(f"----------> 基础数据-组织数据-员工信息-新增 查询常驻地信息 接口响应：{response.text}")
        assert response.status_code == 200, "{} 接口响应状态码不是200".format(
            self.__doc__.split("\n")[1].strip("\t"))
        num = random.randint(0, 4)
        city_id = response.json().get("page").get("list")[num].get("ID")
        return city_id

    def FINDDATAS_SplitWord(
            self,
            baseIp,
            cookie,
            page=1,
            limit=20,
            data=None,
            url="/sys/base/bankunitedcode/pageForSplitWord",
            *args,
            **kwargs):
        api_name = "基础数据-业务数据-客商管理 银行信息-开户行查询"
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        data = f"page={page}&limit={limit}&orderField=&order=&lovKey=&hasCount="
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json().get('page').get('list')

    def FINDDATAS_LE_VENDORS(
            self,
            baseIp,
            cookie,
            page=1,
            limit=20,
            url="/sys/base/lov/findDatas",
            *args,
            **kwargs):
        api_name = "基础数据-业务数据-客商管理 客商分配-核算主体"
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        data = f"page={page}&limit={limit}&orderField=&order=&lovKey=sysVendorMode.findFuLeMulti&hasCount="
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def ADD_VENDORS(
            self,
            baseIp,
            cookie,
            vendorCode,
            vendorName,
            taxNumber,
            data=None,
            url="/sys/base/vendors",
            *args,
            **kwargs):
        """vendorsCompany: 非全局客商时可以传入核算主体id列表用于客商分配核算主体"""
        api_name = "基础数据-业务数据-客商管理 新增"
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        vendorsCompany_list = []
        if kwargs.get('globalVendor') == '1':
            for le_id in kwargs.get('vendorsCompany'):
                dict1 = {
                    "fuName": "核算主体名称",
                    "leId": le_id
                }
                vendorsCompany_list.append(dict1)
        data = {
            "baseInfo": "",
            "vendorType": random.choice(["002", "003"]),
            "vendorCode": vendorCode if vendorCode else f"vendorCode{str(random.randint(0, 1000000))}",
            "vendorName_zh-CN": vendorName if vendorName else f"vendorName{str(random.randint(0, 1000000))}",
            "vendorName_en-US": "无了吧",
            "globalVendor": kwargs.get('globalVendor') or '0',
            "otherInfo": "无了吧",
            "taxNumber": taxNumber if taxNumber else "taxNumber",
            "taxpayerType": str(random.randint(0, 1)),
            "address": "长沙新云",
            "telNum": f"1{time.strftime('%m%d%H%M%S',time.localtime(time.time()))}",
            "billBankName": f"billBankName",
            "billBankAccount": f"{time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))}",
            "internalUnitFlag": str(random.randint(0, 1)),
            "foreignLeId": kwargs.get('foreignLeId'),
            "foreignLeName": kwargs.get('foreignLeName'),
            "languages": [{
                              "language": "zh-CN",
                              "vendorName": vendorName if vendorName else f"vendorName{str(random.randint(0, 1000000))}"
                          },
                          {
                              "language": "en-US",
                              "vendorName": "无了吧"
                          }],
            "vendors2": vendorsCompany_list,
            "vendorsBank": [{
                                "bankAccountName": f"bankAccountName",
                                "bankAccount": f"{time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))}",
                                "subBank": kwargs.get("bankName") or kwargs.get("subBank"),
                                "provinceName": kwargs.get("province") or kwargs.get("provinceName"),
                                "cityName": kwargs.get("city") or kwargs.get("cityName"),
                                "bankType": kwargs.get("bankType") or kwargs.get('bankHeadOffice'),
                                "vendorSitesTypeName": str(random.randint(0, 1)),
                                "defaultFlag": ["DF0001", "DF0002"][random.randint(0, 1)],
                                "bankNum": kwargs.get("bankNum") or kwargs.get('bankTypeCode'),
                                "unitedCode": kwargs.get("unitedCode") or f"{time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))}",
                                "vendorSitesTypeNames": kwargs.get('vendorSitesTypeNames') or "随便对",
                                "defaultFlagName": kwargs.get('defaultFlagName') or "不晓得"
                            }]
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def SIMPLE_QUERY_VENDORS(
            self,
            serverIp,
            cookie,
            data=None,
            url="/sys/base/vendors/listSimple",
            *args,
            **kwargs):
        api_name = "基础数据-业务数据-客商管理 查询"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        page = kwargs.get('page') if kwargs.get('page') else '1'
        limit = kwargs.get('limit') if kwargs.get('limit') else '20'
        vendorType = kwargs.get('vendorType') if kwargs.get(
            'vendorType') else ''
        globalVendor_EQ = kwargs.get('globalVendor_EQ') if kwargs.get(
            'globalVendor_EQ') else ''
        code = kwargs.get('code') if kwargs.get('code') else ''
        name = kwargs.get('name') if kwargs.get('name') else ''
        validityFlag = kwargs.get('validityFlag') if kwargs.get(
            'validityFlag') else ''
        data = f"page={page}&limit={limit}&orderField=&order=&vendorType={vendorType}&globalVendor_EQ={globalVendor_EQ}" \
               f"&vendorCode={code}&vendorName={name}&validityFlag={validityFlag}&internalUnitFlag=&hasCount="
        numsas = 1
        while True:
            response = requests.post(
                timeout=timeout,
                url=request_url,
                headers=headers,
                data=data.encode('utf-8'))
            if "ERROR" not in response.text or numsas > 2:
                break
            numsas += 1

        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return response.json()

    def ADD_VENDORSCOMPANY(
            self,
            serverIp,
            cookie,
            leId, accountId, accountErpCode, accountErpName, vendorId, post='post',
            data=None,
            url="/sys/base/vendorsCompany",
            *args,
            **kwargs):
        api_name = "核算基础-SAP相关配置-客商科目分配 分配科目"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        data = {
            "leId": leId,
            "fuName": kwargs.get("fuName"),
            "accountId": accountId,
            "accountErpName": accountErpName,
            "id": kwargs.get("id"),
            "vendorId": vendorId,
            "accountErpCode": accountErpCode,
            "language": "zh-CN",
            "validityFlag": '0',
            "validityName": "有效",
            "createDate": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "initRowIndex": kwargs.get("initRowIndex")
        }
        if post == 'post':
            response = requests.post(
                timeout=timeout,
                url=request_url,
                headers=headers,
                json=data)
        else:
            response = requests.put(
                timeout=timeout,
                url=request_url,
                headers=headers,
                json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_VENDORSCOMPANY(
            self,
            serverIp,
            cookie,
            data=None,
            url="/sys/base/vendorsCompany/page",
            **kwargs):
        """
        查询条件在ride使用"xxx"="yyy"的格式传入即可
        """
        api_name = "核算基础-SAP相关配置-客商科目分配 分页查询"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        # 原始基础条件
        page = kwargs.get('page') if kwargs.get('page') else '1'
        limit = kwargs.get('limit') if kwargs.get('limit') else '20'
        suite = {'page': page, 'limit': limit}
        kwargs.update(suite)
        dlist = []
        for key, value in kwargs.items():
            dlist.append(key + '=' + value)
        data = "&".join(dlist)

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data.encode('utf-8'))
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return response.json()

    def ADD_TEMP_VENDORS(
            self,
            baseIp,
            cookie,
            vendorName,
            bankAccountName,
            bankAccount,
            subBank="",
            bankType="",
            bankNum="",
            unitedCode="",
            provinceName="湖南",
            cityName="长沙",
            data=None,
            url="/sys/base/vendors/tempVendorBank/",
            *args,
            **kwargs):
        api_name = "基础数据-业务数据-临时供应商 新增"
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        data ={
            "vendorName": vendorName,
            "bankAccountName": bankAccountName,
            "bankAccount": bankAccount,
            "subBank": subBank if subBank else kwargs.get('bankName'),
            "unitedCode": unitedCode if unitedCode else kwargs.get('unitedCode'),
            "provinceName": provinceName if provinceName else kwargs.get('province'),
            "cityName": cityName if cityName else kwargs.get('city'),
            "bankType": bankType if bankType else kwargs.get('bankHeadOffice'),
            "vendorSitesTypeName": str(random.randint(1, 2)),
            "bankNum": bankNum if bankNum else kwargs.get('bankTypeCode')
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def FINDDATAS_RoleMode(
            self,
            baseIp,
            cookie,
            page=1,
            limit=20,
            url="/sys/base/lov/findDatas",
            *args,
            **kwargs):
        api_name = "基础数据-业务数据-项目管理 项目负责人LOV查询"
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        data = f"page={page}&limit={limit}&orderField=&order=&lovKey=sysRoleMode.findUserAccount&hasCount="
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def FINDDATAS_FuLeMulti(
            self,
            baseIp,
            cookie,
            page=1,
            limit=20,
            url="/sys/base/lov/findDatas",
            *args,
            **kwargs):
        api_name = "基础数据-业务数据-项目管理 核算主体LOV查询"
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        data = f"page={page}&limit={limit}&orderField=&order=&lovKey=sysVendorMode.findFuLeMulti&hasCount=N"
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def ADD_PROJECT(
            self,
            baseIp,
            cookie,
            code,
            name,
            principals,
            directors,
            fuId,
            data=None,
            url="/sys/base/project/",
            *args,
            **kwargs):
        api_name = "基础数据-业务数据-项目管理 新增"
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        project_list = ['xm001', 'CWIP', '06', '05', '04', '03', '02', '01', '13', '12', '11', '10', '09', '08', '07']
        projectAttribute_list = ["ITEM_PROJECT", "ADMINISTRATION_PROJECT", "CAPITAL_PROJECT", "INTRUST_PROJECT", "EXPENSE_PROJECT"]
        data = {
            "code": code,
            "name": name,
            "type": kwargs.get('type') if kwargs.get('type') else project_list[random.randint(0, len(project_list)-1)],
            "projectAttribute": kwargs.get('projectAttribute') if kwargs.get('projectAttribute') else projectAttribute_list[random.randint(0, len(projectAttribute_list)-1)],
            "projectBelong": kwargs.get('projectBelong') if kwargs.get('projectBelong') else "003",
            "principals": principals,
            "principalNames": "xxx乱写的",
            "directors": directors,
            "directorNames": "xxx乱写的",
            "publicProject": kwargs.get('publicProject') if kwargs.get('publicProject') else str(random.randint(0, 1)),
            "fuId": fuId,
            "fuName": "xxx乱写的"
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def ADD_FLEXVALUESETS(
            self,
            baseIp,
            cookie,
            flexValueSetCode,
            flexValueSetName,
            data=None,
            url="/sys/base/flexvaluesets",
            **kwargs):
        """
        flexValueSetType: "VST0002"
        dimensionPurposeMulti: "BUDGET^FIELD^STRUCTURES^AUTH"
        """
        api_name = "基础数据-业务数据-维度管理 新增"
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        data = {
                "flexValueSetCode": flexValueSetCode,
                "flexValueSetName": flexValueSetName,
                "flexValueSetType": "VST0002",  # 自定义维度
                "dimensionPurposeMulti": '^'.join(random.sample(["AUTH", "STRUCTURES", "FIELD"], random.randint(1, 3))),
                "applicationTableName": kwargs.get("applicationTableName"),
                "valueColumnCode": kwargs.get("valueColumnCode"),
                "valueColumnName": kwargs.get("valueColumnName"),
                "additionalWhereClause": kwargs.get("additionalWhereClause"),
                "sql": kwargs.get("sql")
            }
        # print(f"---------->{api_name} 接口请求体{data}")
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_FLEXVALUESETS(
            self,
            serverIp,
            cookie,
            data=None,
            url="/sys/base/flexvaluesets/list",
            *args,
            **kwargs):
        api_name = "基础数据-业务数据-维度管理 查询"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        # 原始基础条件
        page = kwargs.get('page') if kwargs.get('page') else '1'
        limit = kwargs.get('limit') if kwargs.get('limit') else '20'
        validityFlag = kwargs.get('validityFlag') if kwargs.get('validityFlag') else '0'
        suite = {'page': page, 'limit': limit, 'validityFlag': validityFlag}
        kwargs.update(suite)
        dlist = []
        for key, value in kwargs.items():
            dlist.append(str(key) + '=' + str(value))
        data = "&".join(dlist)
        # print(f"---------->{api_name} 接口请求体{data.encode('utf-8')}")
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data.encode('utf-8'))
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return self.GET_VALUE_FROM_JSON_DICT(response.json(), "id")

    def ADD_FLEXVALUESETS_CUSTOM(
            self,
            baseIp,
            cookie,
            valueCode, valueRemark_zh_CN, valueSetId,
            data=None,
            url="/sys/base/flexvaluesetscustom",
            **kwargs):
        """
        flexValueSetType: "VST0002"
        dimensionPurposeMulti: "BUDGET^FIELD^STRUCTURES^AUTH"
        """
        api_name = "基础数据-业务数据-维度管理-成员管理 新增"
        request_url = baseIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        data = {
            "valueCode": valueCode,
            "valueRemark_zh-CN": valueRemark_zh_CN,
            "valueRemark_en-US": kwargs.get("valueRemark_en_US"),
            "fId": kwargs.get("fId"),
            "fCode": kwargs.get("fCode"),
            "fRemark": kwargs.get("fRemark"),
            "leId": kwargs.get("leId"),
            "leName": kwargs.get("leName"),
            "valueSetId": valueSetId,
            "languages": [
                {
                    "language": "zh-CN",
                    "valueRemark": valueRemark_zh_CN
                },
                {
                    "language": "en-US",
                    "valueRemark": kwargs.get("valueRemark")
                }
            ]
        }

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def ADD_SCHEME(
            self,
            serverIp,
            cookie,
            schemeCode,
            schemeName,
            data=None,
            url="/sys/base/coaaccountscheme",
            **kwargs):
        """
        schemeCode：科目编码
        schemeName：科目名称
        """
        api_name = "基础数据-财务数据-科目体系 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)

        data = {
            "schemeCode": schemeCode,
            "schemeName": schemeName
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_SCHEME(
            self,
            serverIp,
            cookie,
            data=None,
            url="/sys/base/coaaccountscheme/page",
            **kwargs):
        """
        查询条件在ride使用"xxx"="yyy"的格式传入即可
        """
        api_name = "基础数据-财务数据-科目体系 分页查询"
        request_url = serverIp  + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        # 原始基础条件
        page = kwargs.get('page') if kwargs.get('page') else '1'
        limit = kwargs.get('limit') if kwargs.get('limit') else '20'
        suite = {'page': page, 'limit': limit}
        kwargs.update(suite)
        dlist = []
        for key, value in kwargs.items():
            dlist.append(str(key) + '=' + str(value))
        data = "&".join(dlist)

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data.encode('utf-8'))
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return self.GET_VALUE_FROM_JSON_DICT(response.json(), 'id')

    def QUERY_COA(
            self,
            serverIp,
            cookie,
            data=None,
            url="/sys/base/coaaccounterp/page",
            **kwargs):
        """
        查询条件由不定长参数接收.
        在ride使用"xxx"="yyy"的格式传入即可.
        """
        api_name = "基础数据-财务数据-科目体系菜单-科目配置 分页查询"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        # 原始基础条件
        page = kwargs.get('page') if kwargs.get('page') else '1'
        limit = kwargs.get('limit') if kwargs.get('limit') else '20'
        suite = {'page': page, 'limit': limit}
        kwargs.update(suite)
        dlist = []
        for key, value in kwargs.items():
            dlist.append(str(key) + '=' + str(value))
        data = "&".join(dlist)

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data.encode('utf-8'))
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return response.json()

    def ADD_COA(
            self,
            serverIp,
            cookie,
            accountCode,
            accountName,
            accountSchemeId,
            pid="",
            data=None,
            url="/sys/base/coaaccounterp",
            **kwargs):
        """
        schemeCode：科目编码
        schemeName：科目名称
        accountSchemeId：科目体系的id
        pid：上级科目的id，非必填
        accountType：科目类型
        hierarchy：是否末级，0是，1否
        glflag：与特殊总账业务相关，0是，1否
        specialPurposeId：科目特殊用途的id
        specialPurposeType：科目特殊用途的类型
        specialPurposeTypeName：科目特殊用途的名字
        """
        api_name = "基础数据-财务数据-科目体系菜单-科目配置 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)

        data = {
            "accountCode": accountCode,
            "accountName": accountName,
            "pid": pid,
            "pAccountName": kwargs.get('pAccountName'),
            "accountType": ['D', 'K', 'S', 'A'][random.randint(0, 3)],
            "glFlag": str(random.randint(0, 1)),
            "hierarchy": kwargs.get('hierarchy') or '0',  # 0:末级 才能被用 # str(random.randint(0, 1)),
            "accountSchemeId": accountSchemeId,
            "specialPurposeId": kwargs.get('specialPurposeId'),
            "specialPurposeType": kwargs.get('specialPurposeType'),
            "specialPurposeTypeName": kwargs.get('specialPurposeTypeName'),
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def ADD_EXCHANGERATE(
            self,
            serverIp,
            cookie,
            code, rateSchemeName, basicCurrencyId,
            data=None,
            url="/sys/base/exchangerate",
            **kwargs):
        """
        schemeCode：汇率编码
        schemeName：汇率名称
        accountSchemeId：汇率体系的id
        pid：上级汇率的id，非必填
        accountType：汇率类型
        hierarchy：是否末级，0是，1否
        glflag：与特殊总账业务相关，0是，1否
        specialPurposeId：汇率特殊用途的id
        specialPurposeType：汇率特殊用途的类型
        specialPurposeTypeName：汇率特殊用途的名字
        """
        api_name = "基础数据-财务数据-税率方案 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)

        data = {
            "rateSchemeName_zh-CN": rateSchemeName,
            "rateSchemeName_en-US": kwargs.get("rateSchemeName_en_US"),
            "code": code,
            "basicCurrencyId": basicCurrencyId,
            "currencyName": kwargs.get("currencyName"),
            "exactLength": kwargs.get("exactLength") or "0",
            "ratePeriod": ["EC0001", "EC0002"][random.randint(0, 1)],
            "languages": [
                {
                    "language": "zh-CN",
                    "rateSchemeName": rateSchemeName
                },
                {
                    "language": "en-US",
                    "rateSchemeName": kwargs.get("rateSchemeName_en_US")
                }
            ]
        }
        # print(f"----------> {api_name} 接口请求体：{data}")
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_EXCHANGERATE(
            self,
            serverIp,
            cookie,
            data=None,
            url="/sys/base/exchangerate/list",
            **kwargs):
        """
        查询条件由不定长参数接收.
        在ride使用"xxx"="yyy"的格式传入即可.
        """
        api_name = "基础数据-财务数据-税率方案 分页查询"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        # 原始基础条件
        page = kwargs.get('page') if kwargs.get('page') else '1'
        limit = kwargs.get('limit') if kwargs.get('limit') else '20'
        suite = {'page': page, 'limit': limit}
        kwargs.update(suite)
        dlist = []
        for key, value in kwargs.items():
            dlist.append(str(key) + '=' + str(value))
        data = "&".join(dlist)

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data.encode('utf-8'))
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return self.GET_DICT(response.json(), 'id') or ""

    def ADD_RATECON(
            self,
            serverIp,
            cookie,
            rateSchemeId,
            scalerCurrencyCode="CNY",
            rateValue="0.0001",
            rateMagnification="0",
            rateDate="",
            data=None,
            url="/sys/base/exchangerate/config",
            **kwargs):
        """
        rateSchemeId：汇率方案的id
        rateDate："yyyy-mm-dd"，不传默认取今天
        scalerCurrencyCode：币种编码
        rateValue：汇率
        rateMagnification：放大倍数
        """
        api_name = "基础数据-财务数据-汇率方案菜单-配置 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        try:
            if not isinstance(eval(rateMagnification), int):
                print("rateMagnification放大倍数 请传整数")
                raise BaseException
        except BaseException:
            return
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        if rateDate == "":
            rateDate = datetime.datetime.today().strftime('%Y-%m-%d')  # 取今日日期，格式yyyy-mm-dd
        data = {
            "rateSchemeId": rateSchemeId,
            "rateDate": rateDate,
            "scalerCurrencyCode": scalerCurrencyCode,
            "scalerCurrencyName": kwargs.get("scalerCurrencyName"),
            "rateValue": eval(rateValue),
            "rateMagnification": int(rateMagnification)
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()
    def ADD_SOB(
            self,
            serverIp,
            cookie,
            sobCode,
            sobName,
            coaSchemeId,
            rateSchemeId,
            erpType=None,
            data=None,
            url="/sys/base/sob/",
            **kwargs):
        """
        sobCode：账套编码
        sobName：账套名称
        coaSchemeId：科目体系id
        rateSchemeId：汇率方案id
        useCurrencyCode：可使用币种，如"TWD^CUP"
        erpType：erp类型，如"UFIDA_U8"
        **kwargs支持：expenseAccountId借方科目id、capitalAccountId贷方科目id等
        """
        api_name = "基础数据-财务数据-账套管理 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        erp_list = ['SAP', 'KINGDEE_K3', 'KINGDEE_EAS', 'EBS', 'UFIDA_NC', 'UFIDA_U8']
        data = {
            "sobCode": sobCode,
            "name_zh-CN": sobName,
            "name_en-US": kwargs.get("name_en_US") if kwargs.get("name_en_US") else kwargs.get("name_en-US"),
            "coaSchemeId": coaSchemeId,
            "schemeName": kwargs.get("schemeName") if kwargs.get("schemeName") else "",
            "expenseAccountId": kwargs.get("expenseAccountId") if kwargs.get("expenseAccountId") else "",
            "expenseAccountName": kwargs.get("expenseAccountName") if kwargs.get("expenseAccountName") else "",
            "capitalAccountId": kwargs.get("capitalAccountId") if kwargs.get("capitalAccountId") else "",
            "capitalAccountName": kwargs.get("capitalAccountName") if kwargs.get("capitalAccountName") else "",
            "rateSchemeId": rateSchemeId,
            "rateSchemeName": kwargs.get("rateSchemeName") if kwargs.get("rateSchemeName") else "",
            "useCurrencyCode": kwargs.get("useCurrencyCode"),
            "useCurrencyNames": kwargs.get("useCurrencyNames") if kwargs.get("useCurrencyNames") else "",
            "currencyName": kwargs.get("currencyName") if kwargs.get("currencyName") else "",
            "erpType": erpType if erpType else erp_list[random.randint(0, len(erp_list)-1)],
            "displayAdjustmentPeriod": kwargs.get("displayAdjustmentPeriod") if kwargs.get(
                "displayAdjustmentPeriod") else "1",
            "remark_zh-CN": kwargs.get("remark_zh_CN") if kwargs.get("remark_zh_CN") else "remark_zh-CN",
            "remark_en-US": kwargs.get("remark_en_US") if kwargs.get("remark_en_US") else kwargs.get("remark_en-US"),
            "currencyCode": kwargs.get("currencyCode"),
            "languages": [{
                              "language": "zh-CN",
                              "name": sobName,
                              "remark": kwargs.get("remark_zh_CN") if kwargs.get("remark_zh_CN") else "remark_zh-CN",
                          },
                          {
                              "language": "en-US",
                              "name": kwargs.get("name_en_US") if kwargs.get("name_en_US") else kwargs.get(
                                  "name_en-US"),
                              "remark": kwargs.get("remark_en_US") if kwargs.get("remark_en_US") else kwargs.get(
                                  "remark_en-US"),
                          }]
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_SOB(
            self,
            serverIp,
            cookie,
            data=None,
            url="/sys/base/sob/page",
            **kwargs):
        """
        查询条件在ride使用"xxx"="yyy"的格式传入即可
        """
        api_name = "基础数据-财务数据-账套管理 分页查询"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        # 原始基础条件
        page = kwargs.get('page') if kwargs.get('page') else '1'
        limit = kwargs.get('limit') if kwargs.get('limit') else '20'
        suite = {'page': page, 'limit': limit}
        kwargs.update(suite)
        dlist = []
        for key, value in kwargs.items():
            dlist.append(str(key) + '=' + str(value))
        data = "&".join(dlist)

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data.encode('utf-8'))
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return response.json()

    def CREATE_SOBS(self, base_url, cookie, num_sob_makeing=0, namedFlag=''):
        """批量创建多个账套"""
        # 获取币种列表
        msg = ''
        result1 = self.FINDDATA(base_url, cookie,
                                data='page=1&limit=50&orderField=&order=&lovKey=sysCurrencyMode.findNameAndCodeSingle&hasCount=N')
        CURRENCY_CODE_list = self.GET_VALUE_FROM_JSON_DICT(result1, 'CURRENCY_CODE')  # 币种列表
        # 获取汇率方案exchangerate和本位币currencyCode
        concurrent_ = self.FINDDATA(base_url, cookie,
                                    data="page=1&limit=50&orderField=&order=&lovKey=sysExchangeRateMode.findNameAndCode&hasCount=N")
        list_BASIC_CURRENCY_ID = self.GET_VALUE_FROM_JSON_DICT(concurrent_, 'ID')
        list_BASIC_CURRENCY_CODE = self.GET_VALUE_FROM_JSON_DICT(concurrent_, 'BASIC_CURRENCY_CODE')
        ID_CODE_LIST = [list(t) for t in zip(list_BASIC_CURRENCY_ID, list_BASIC_CURRENCY_CODE)]
        # 获取科目体系coaaccountscheme
        result = self.FINDDATA(base_url, cookie,
                               "page=1&limit=1000&orderField=&order=&lovKey=sysCoaAccountSchemeMode.findNameAndCode&hasCount=N")
        list_coaaccountscheme = self.GET_VALUE_FROM_JSON_DICT(result, 'ID')
        sobId_list = []
        for num_v in range(1, num_sob_makeing + 1):
            random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数8位
            this_code = f"{namedFlag}sob{str(random_num)}"
            this_name = f"{namedFlag}账套名称{str(random_num)}"
            # 挑一个币种
            curr_code = random.choice(CURRENCY_CODE_list)
            # 挑一个汇率方案exchangerate和本位币
            saas = random.choice(ID_CODE_LIST)
            exchangerate = saas[0]  # 汇率方案
            currencyCode = saas[-1]  # 本位币
            # 挑一个科目体系
            coaaccountscheme = random.choice(list_coaaccountscheme)
            if not self.QUERY_COA(base_url, cookie, accountSchemeId=coaaccountscheme).get('page').get('totalCount'):
                this_Coacode = f"{namedFlag}Coa{str(random_num)}"
                this_Coaname = f"{namedFlag}会计科目名称{str(random_num)}"
                self.ADD_COA(base_url, cookie, this_Coacode, this_Coaname, coaaccountscheme)
            num_this = 1
            while True:
                result = self.ADD_SOB(base_url, cookie, this_code, this_name, coaaccountscheme, exchangerate,
                                      currencyCode=currencyCode, useCurrencyCode=curr_code)
                if result.get("msg") == "字段长度超过限制":
                    sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff',
                                           auto_close_duration=5)
                    return "字段长度超出限制，请控制下长度[末尾特征标记]"
                if result.get('msg') == 'success':
                    result23 = self.QUERY_SOB(base_url, cookie, validityFlag='0')
                    sobId = self.GET_DICT(result23, 'id')
                    sobId_list.append(sobId)
                else:
                    if num_this >= 3:
                        msg_ = result.get('msg')
                        if msg_:
                            msg += msg_ + '\n'
                        break
                    else:
                        num_this += 1
                        continue
            if num_this >= 3:
                break
        return sobId_list

    def GET_COAINFO_MUST(self, base_url, cookie, leCode='', coaSchemeId='',num_coa_makeing=1, namedFlag=''):
        """给核算主体编码或者账套id，返回核算主体对应账套对应科目体系下的科目id\name\code"""
        result53 = self.FINDDATA(base_url, cookie, f'page=1&limit=5&orderField=&order=&CODE={leCode}&lovKey=sysFuLeMode.findNameAndCode&SOB_ID=')
        if not coaSchemeId:
            coaSchemeId = self.GET_DICT(result53, 'COA_SCHEME_ID')
        result032 = self.QUERY_COA(base_url, cookie, accountSchemeId=coaSchemeId)
        if not result032.get('page').get('totalCount'):
            for i in range(num_coa_makeing):
                random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数8位
                this_Coacode = f"{namedFlag}Coa{str(random_num)}"
                this_Coaname = f"{namedFlag}会计科目名称{str(random_num)}"
                self.ADD_COA(base_url, cookie, this_Coacode, this_Coaname, coaSchemeId)
            result032 = self.QUERY_COA(base_url, cookie, accountSchemeId=coaSchemeId)
        id_list = self.GET_VALUE_FROM_JSON_DICT(result032, 'id')
        name_list = self.GET_VALUE_FROM_JSON_DICT(result032, 'accountName')
        code_list = self.GET_VALUE_FROM_JSON_DICT(result032, 'accountCode')
        # list(zip(id_list, name_list, code_list))
        return list(zip(id_list, name_list, code_list))
    def ADD_FULECC(
            self,
            serverIp,
            cookie,
            code, name_zh_CN, sobId="",
            data=None,
            url="/sys/base/fulecc",
            **kwargs):
        """
        schemeCode：汇率明细编码
        schemeName：汇率明细名称
        accountSchemeId：汇率明细体系的id
        pid：上级汇率明细的id，非必填
        accountType：汇率明细类型
        hierarchy：是否末级，0是，1否
        glflag：与特殊总账业务相关，0是，1否
        specialPurposeId：汇率明细特殊用途的id
        specialPurposeType：汇率明细特殊用途的类型
        specialPurposeTypeName：汇率明细特殊用途的名字
        """
        api_name = "基础数据-财务数据-财务组织 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)

        data = { # sobName,affiliatedFuleName,validityFlag,closeFlag,pid,fuType,affiliatedFuleId,
            "code": code,
            "name_zh-CN": name_zh_CN,
            "name_en-US": kwargs.get("name_en_US"),
            "sobName": kwargs.get("sobName") or name_zh_CN,
            "legalPerson": kwargs.get("legalPerson"),
            "legalPersonName": kwargs.get("legalPersonName"),
            "taxPayerType": kwargs.get("taxPayerType") or '0',
            "remark_zh-CN": kwargs.get("remark_zh_CN"),
            "remark_en-US": kwargs.get("remark_en_US"),
            "validityFlag": kwargs.get("validityFlag") or '0',
            "fuType": kwargs.get("fuType") or '0',
            "sobId": sobId,
            "affiliatedFuleId": kwargs.get("affiliatedFuleId") or sobId,
            "affiliatedFuleName": kwargs.get("affiliatedFuleName"),
            "closeFlag": kwargs.get("closeFlag") or '0',
            "pid": kwargs.get("pid") or sobId,
            "languages": [
                {
                    "language": "zh-CN",
                    "name": kwargs.get("name_zh_CN") or name_zh_CN,
                    "remark": kwargs.get("remark")
                },
                {
                    "language": "en-US",
                    "name": kwargs.get("name_en_US"),
                    "remark": kwargs.get("remark")
                }
            ]
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_FULECC(
            self,
            serverIp,
            cookie,
            data=None,
            url="/sys/base/fulecc/findParentByName",
            **kwargs):
        """
        查询条件由不定长参数接收.
        在ride使用"xxx"="yyy"的格式传入即可.
        """
        api_name = "基础数据-财务数据-财务组织 分页查询"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        if kwargs.get('code'):
            data = {
                "code": kwargs.get('code')
            }
        else:
            data = {
                "name": kwargs.get('name')
            }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return response.json()

    def BILL_TYPE_INVOICE_TAXRATE(
        self,
        serverIp,
        cookie,
        data=None,
        url="/sys/common/select/list/BILL_TYPE_INVOICE"):
        """
        基础数据-财务数据-税种税率菜单，票据类型查询接口
        data_type:账套名称:0、核算主体:1、业务大类:2，科目名称:3， 或者直接传lovKey的值
        data: 传入其余字段，dict
        """
        api_name = "票据类型 字段查询接口"
        request_url = serverIp + url
        headers = {"Cookie": cookie}
        response = requests.get(
            timeout=timeout,
            url=request_url,
            headers=headers)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()


    def TAX_RATE_TAXRATE(
        self,
        serverIp,
        cookie,
        url="/sys/common/select/list/TAX_RATE"):
        """
        基础数据-财务数据-税种税率菜单，税率(%)查询接口
        data_type:账套名称:0、核算主体:1、业务大类:2，科目名称:3， 或者直接传lovKey的值
        data: 传入其余字段，dict
        """
        api_name = "税率(%) 字段查询接口"
        request_url = serverIp + url
        headers = {"Cookie": cookie}
        response = requests.get(
            timeout=timeout,
            url=request_url,
            headers=headers)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def IS_SPECIAL_TICKET_TAXRATE(
        self,
        serverIp,
        cookie,
        data=None,
        url="/sys/common/select/list/IS_SPECIAL_TICKET"):
        """
        基础数据-财务数据-税种税率菜单，税率(%)查询接口
        data_type:账套名称:0、核算主体:1、业务大类:2，科目名称:3， 或者直接传lovKey的值
        data: 传入其余字段，dict
        """
        api_name = "税率(%) 字段查询接口"
        request_url = serverIp + url
        headers = {"Cookie": cookie}
        response = requests.get(
            timeout=timeout,
            url=request_url,
            headers=headers)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def ADD_TAXRATE(
            self,
            serverIp,
            cookie,
            taxCode,  # 税率编码
            billTypeInvoice,  # 发票类型
            pid,  # 父节点
            taxValue="",
            data=None,
            url="/sys/base/taxrate",
            **kwargs):
        api_name = "基础数据-财务数据-税种税率菜单 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)

        data = {
            "taxCode": taxCode,
            "billTypeInvoice": billTypeInvoice,
            "billTypeInvoiceName": kwargs.get("billTypeInvoiceName"),
            "taxValue": taxValue,
            "isSpecialTicket": str(random.randint(0, 1)),
            "invoiceOperation": str(random.randint(0, 1)),
            "validityFlag": kwargs.get("validityFlag"),
            "taxType": kwargs.get("taxType"),
            "pid": pid,
            "languages": [
                {"language": "zh-CN"},
                {"language": "en-US"}
            ]
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_TAXRATE(
            self,
            serverIp,
            cookie,
            taxCode,
            order='asc',
            url="/sys/base/taxrate/sysTaxRateTreeSort",
            **kwargs):
        """
        按编码查询id  order:正序asc，逆序desc
        """
        api_name = "基础数据-财务数据-税种税率菜单 按编码排序"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json"
        }
        data = {"order": order,
                "orderField": "taxCode"
                }

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return self.GET_DICT(response.json(), 'id', taxCode=taxCode)

    def ADD_COMPANYBANK(
            self,
            serverIp,
            cookie,
            fuId, code, subBank, currencyId, sobId, bankAccountNum, bankLevel, bankTypeCode,
            data=None,
            url="/sys/base/companybank",
            **kwargs):
        api_name = "基础数据-财务数据-公司账户 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)

        data = {
            "bankLevel": bankLevel,
            "fuId": fuId,
            "fuLeName": kwargs.get("fuLeName"),
            "code": code,
            "subBank": subBank,
            "subBankName": kwargs.get("subBankName"),
            "bankAccountName": kwargs.get("bankAccountName"),
            "bankAccountNum": bankAccountNum,
            "collectionType": ["RP0001", "RP0002", "RP0003"][random.randint(0, 2)],
            "currencyId": currencyId,
            "currencyName": kwargs.get("currencyName"),
            "bankAccountType": ['AN0001', 'AN0002', 'AN0003'][random.randint(0, 2)],
            "isConnAccount": ['N', 'Y'][random.randint(0, 1)],
            "isConnTicket": ['N', 'Y'][random.randint(0, 1)],
            "sobId": sobId,
            "sobName": kwargs.get("sobName"),
            "bankTypeCode": bankTypeCode
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def ADD_PAYMENTMODE(
            self,
            serverIp,
            cookie,
            paymentSort, paymentCode, paymentName_zh_CN,
            data=None,
            url="/sys/base/paymentmode",
            **kwargs):
        api_name = "基础数据-财务数据-结算方式 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)

        data = {
            "paymentSort": paymentSort,
            "paymentCode": paymentCode,
            "paymentName_zh-CN": paymentName_zh_CN,
            "paymentName_en-US": paymentName_zh_CN,
            "onAccountFlag": str(random.randint(0, 1)),
            "transferFundFlag": ['001', '002', '003', '004', '005', '007', '008'][random.randint(0, 6)],
            "bankAccountFlag": str(random.randint(0, 1)),
            "languages": [
                {"language": "zh-CN",
                 "paymentName": paymentName_zh_CN},
                {"language": "en-US",
                 "paymentName": paymentName_zh_CN}
            ]
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_PAYMENTMODE(
            self,
            serverIp,
            cookie,
            data=None,
            url="/sys/base/paymentmode/list",
            **kwargs):
        """
        查询条件在ride使用"xxx"="yyy"的格式传入即可
        """
        api_name = "基础数据-财务数据-结算方式 分页查询"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        # 原始基础条件
        page = kwargs.get('page') if kwargs.get('page') else '1'
        limit = kwargs.get('limit') if kwargs.get('limit') else '50'
        suite = {'page': page, 'limit': limit}
        kwargs.update(suite)
        dlist = []
        for key, value in kwargs.items():
            dlist.append(str(key) + '=' + str(value))
        data = "&".join(dlist)

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data.encode('utf-8'))
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return response.json()

    def ADD_CATEGORY(
            self,
            serverIp,
            cookie,
            code,
            name_zh_CN,
            pid='0',
            url="/sys/base/boe/base",
            *args,
            **kwargs):
        """
        新增业务大/小类
        :param serverIp: IP地址
        :param cookie: cookie信息
        :param code: 业务类型编码
        :param name_zh_CN: 业务类型名称
        :param pid: 业务大类pid
        :param protocol: 协议，默认为http
        :param port: 端口号，默认为空
        :param url: 接口地址，默认为/sys/base/boe/base
        :param args: 不定长参数
        :param kwargs: 关键字参数
        :return: response.json()
        """
        request_url = serverIp + url
        headers = {
            "Content-Type": "application/json",
            "Cookie": cookie
        }
        data = {
            "code": code,
            "name_zh-CN": name_zh_CN,
            "name_en-US": kwargs.get("name_en-US") or kwargs.get("name_en_US"),
            "icon": kwargs.get("icon"),
            "appShow": kwargs.get("appShow"),
            "appName_zh-CN": kwargs.get("appName_zh-CN") or kwargs.get("appName_zh_CN"),
            "appName_en-US": kwargs.get("appName_en-US") or kwargs.get("appName_zh_US"),
            "validityFlag": kwargs.get("validityFlag") or "0",
            "auditPoints_zh-CN": kwargs.get("auditPoints_zh-CN") or kwargs.get("auditPoints_zh_CN"),
            "auditPoints_en-US": kwargs.get("auditPoints_en-US") or kwargs.get("auditPoints_en_US"),
            "remark_zh-CN": kwargs.get("remark_zh-CN") or kwargs.get("remark_zh_CN"),
            "remark_en-US": kwargs.get("remark_en-US") or kwargs.get("remark_en_US"),
            "attribute": kwargs.get("attribute"),
            "statisticalDimensionId": kwargs.get("statisticalDimensionId"),
            "type": kwargs.get("type") or "0",
            "pid": pid,
            "languages": [{
                              "language": "zh-CN",
                              "name": name_zh_CN,
                              "appName": kwargs.get("appName_zh-CN") or kwargs.get("appName_zh_CN"),
                              "auditPoints": kwargs.get("auditPoints_zh-CN") or kwargs.get("auditPoints_zh_CN"),
                              "remark": kwargs.get("remark_zh-CN") or kwargs.get("remark_zh_CN")
                          },
                          {
                              "language": "en-US",
                              "name": kwargs.get("name_en-US") or kwargs.get("name_en_US"),
                              "appName": kwargs.get("appName_en-US") or kwargs.get("appName_zh_US"),
                              "auditPoints": kwargs.get("auditPoints_en-US") or kwargs.get("auditPoints_en_US"),
                              "remark": kwargs.get("remark_en-US") or kwargs.get("remark_en_US")
                          }]
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=json.dumps(data))
        # print(f"----------> 新增业务大/小类响应：{response.json()}")
        assert response.status_code == 200, "{} 接口响应状态码不是200".format(
            self.__doc__.split("\n")[1].strip("\t"))
        return response.json()

    def ADD_BIZ2ACCOUNT(
            self,
            serverIp,
            cookie,
            sob_list,  # sobId, coaSchemeId
            bizCategoryId_list,  # bizCategoryId
            accountType_list,  # accountType
            goal,
            data=None,
            url="/sys/base/bizaccountsubject",
            **kwargs):
        api_name = "基础配置-核算基础-会计科目配置菜单-业务科目配置 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        times = 0
        for x in sob_list:
            sobId, coaSchemeId = x[0], x[1]
            for accountType in accountType_list:
                for bizCategoryId in bizCategoryId_list:
                    if data:
                        if isinstance(data, str):
                            kwargs.update(eval(data))
                        if isinstance(data, dict):
                            kwargs.update(data)
                    result2 = self.QUERY_COA(serverIp, cookie, accountSchemeId=coaSchemeId)
                    accountId= self.GET_DICT(result2, 'id')
                    accountCode = self.GET_DICT(result2, 'accountCode')
                    # accountType = self.GET_DICT(result2, 'accountType')
                    data = {
                        "sobId": sobId,
                        "sobName": kwargs.get("sobName"),
                        "fuId": kwargs.get("fuId"),
                        "fuName": kwargs.get("fuName"),
                        "bizCategoryId": bizCategoryId,
                        "bizCategoryName": kwargs.get("bizCategoryName"),
                        "accountType": accountType,
                        "accountId": accountId,
                        "accountName": kwargs.get("accountName"),
                        "funRangeType": kwargs.get("funRangeType"),
                        "projectCode": kwargs.get("projectCode"),
                        "coaSchemeId": coaSchemeId,
                        "accountCode": accountCode
                    }
                    response = requests.post(
                        timeout=timeout,
                        url=request_url,
                        headers=headers,
                        json=data)
                    # print(f"----------> {api_name} 接口响应：{response.text}")
                    assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
                    times += 1
                    list_base_num_more[0] += 1
                    if times >= goal:
                        return f'造数完成，造数{times}次'
        return f'造数失败，造数{times}次'

    def ADD_TAXRATEC(
            self,
            serverIp,
            cookie,
            sob_list,  # sobId, coaSchemeId
            taxRateId_list,  # taxRateId税种税率
            accountType_list,  # accountType
            goal,
            data=None,
            url="/sys/base/taxRateC",
            **kwargs):
        api_name = "基础配置-核算基础-会计科目配置菜单-税务科目配置 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        times = 0
        for x in sob_list:
            sobId, coaSchemeId = x[0], x[1]
            for accountType in accountType_list:
                for taxRateId in taxRateId_list:
                    if data:
                        if isinstance(data, str):
                            kwargs.update(eval(data))
                        if isinstance(data, dict):
                            kwargs.update(data)
                    result2 = self.QUERY_COA(serverIp, cookie, accountSchemeId=coaSchemeId)
                    accountId= self.GET_DICT(result2, 'id')
                    accountCode = self.GET_DICT(result2, 'accountCode')

                    data = {
                        "sobId": sobId,
                        "sobName": kwargs.get("sobName"),
                        "deptId": kwargs.get("deptId"),
                        "fuName": kwargs.get("fuName"),
                        "taxRateId": taxRateId,
                        "taxRateName": kwargs.get("taxRateName"),
                        "accountId": accountId,
                        "accountName": kwargs.get("accountName"),
                        "taxItemCode": kwargs.get("taxItemCode"),
                        "accountType": accountType,
                        "coaSchemeId": coaSchemeId,
                        "accountCode": accountCode
                    }
                    response = requests.post(
                        timeout=timeout,
                        url=request_url,
                        headers=headers,
                        json=data)
                    # print(f"----------> {api_name} 接口响应：{response.text}")
                    assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
                    times += 1
                    list_base_num_more[1] += 1
                    if times >= goal:
                        return f'造数完成，造数{times}次'
        return f'造数失败，造数{times}次'

    def ADD_PAYMODEACCT(
            self,
            serverIp,
            cookie,
            sob_list,  # sobId, coaSchemeId
            payment_list,  # paymentModeId,TRANSFER_FUND_FLAG
            goal,
            data=None,
            url="/sys/base/paymentmodeaccount",
            **kwargs):
        api_name = "基础配置-核算基础-会计科目配置菜单-结算科目配置 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        times = 0
        for x in sob_list:
            sobId, coaSchemeId = x[0], x[1]
            for y in payment_list:
                paymentModeId, transferFundFlag = y[0], y[1]
                if data:
                    if isinstance(data, str):
                        kwargs.update(eval(data))
                    if isinstance(data, dict):
                        kwargs.update(data)
                result2 = self.QUERY_COA(serverIp, cookie, accountSchemeId=coaSchemeId)
                subjectId= self.GET_DICT(result2, 'id')
                accountCode = self.GET_DICT(result2, 'accountCode')
                data = {
                    "sobId": sobId,
                    "sobName": kwargs.get("sobName"),
                    "fuId": kwargs.get("fuId"),
                    "leName": kwargs.get("leName"),
                    "paymentModeId": paymentModeId,
                    "paymentModeName": kwargs.get("paymentModeName"),
                    "subjectId": subjectId,
                    "accountName": kwargs.get("accountName"),
                    "coaSchemeId": coaSchemeId,
                    "accountCode": accountCode,
                    "transferFundFlag": transferFundFlag
                }
                response = requests.post(
                    timeout=timeout,
                    url=request_url,
                    headers=headers,
                    json=data)
                # print(f"----------> {api_name} 接口响应：{response.text}")
                assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
                times += 1
                list_base_num_more[2] += 1
                if times >= goal:
                    return f'造数完成，造数{times}次'
        return f'造数失败，造数{times}次'

    def QUERY_CATEGORY_TREE(
            self,
            serverIp,
            cookie,
            url="/sys/base/boe/base/categoryTree",
            **kwargs):
        api_name = "查询业务类型树"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json={})
        # print(f"----------> {api_name} 接口响l")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_BIZ_ACCOUNT_TYPE(
            self,
            serverIp,
            cookie,
            url="/sys/common/select/list/BIZ_ACCOUNT_TYPE",
            **kwargs):
        api_name = "查业务科目类型"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie
        }

        response = requests.get(
            timeout=timeout,
            url=request_url,
            headers=headers)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_TAXRATE_TREE(
            self,
            serverIp,
            cookie,
            url="/sys/base/taxrate/sysTaxRateTreeSort",
            **kwargs):
        api_name = "查询业务类型树"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json={"validityFlag":"0"})
        # print(f"----------> {api_name} 接口响l")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def ADD_COMP2BANK(
            self,
            serverIp,
            cookie,
            companyBankId,
            bankAccountNum,
            leId,
            coaSchemeId,
            data=None,
            url="/sys/base/companybankaccount",
            **kwargs):
        api_name = "基础配置-核算基础-会计科目配置菜单-银行&票据科目配置 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)

        data = {
            "companyBankId": companyBankId,
            "bankAccountName": kwargs.get("bankAccountName"),
            "accountId": kwargs.get("accountId"),
            "accountName": kwargs.get("accountName"),
            "ticketId": kwargs.get("ticketId"),
            "ticketName": kwargs.get("ticketName"),
            "bankAccountNum": bankAccountNum,
            "leId": leId,
            "coaSchemeId": coaSchemeId,
            "accountCode": kwargs.get("accountCode"),
            "ticketCode": kwargs.get("ticketCode")
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_COMPANYBANK(
            self,
            serverIp,
            cookie,
            data=None,
            url="/sys/base/companybank/page",
            **kwargs):
        """
        查询条件在ride使用"xxx"="yyy"的格式传入即可
        """
        api_name = "基础数据-财务数据-公司账户 分页查询"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        # 原始基础条件
        page = kwargs.get('page') if kwargs.get('page') else '1'
        limit = kwargs.get('limit') if kwargs.get('limit') else '20'
        suite = {'page': page, 'limit': limit}
        kwargs.update(suite)
        dlist = []
        for key, value in kwargs.items():
            dlist.append(str(key) + '=' + str(value))
        data = "&".join(dlist)

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data.encode('utf-8'))
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return response.json()

    def ADD_ACCOUNTSTRUCTURES(
            self,
            serverIp,
            cookie,
            structureCode,
            structureName,
            data=None,
            url="/sys/base/accountstructures",
            *args,
            **kwargs):
        """
        基础配置-核算基础-凭证配置-会计结构建立 新增会计结构(基本信息)
        \n:param structureCode: 编码
        \n:param structureName: 名称

        如果要传入其他参数，请参照下列示例：
                分隔符：delimiter=D001  枚举值：{'D001': '-'}
        """
        request_url = serverIp +url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json"
        }
        if data:
            kwargs.update(eval(data))
        namedFlag = kwargs.get("namedFlag")
        random_num = self.random_character(char_type="str", length="8")
        this_code = f"{namedFlag}jgxx{str(random_num)}"
        this_name = f"{namedFlag}结构信息{str(random_num)}"
        result23 = self.FINDDATA(serverIp, cookie, 'page=1&limit=5&orderField=&order=&lovKey=sysAccountStructures.'
                   'findFlexvaluesets&DIMENSION_PURPOSE_MULTI=STRUCTURES&VALIDITY_FLAG=0&hasCount=N')
        flexValueSetsId = random.choice(self.GET_VALUE_FROM_JSON_DICT(result23, 'ID'))
        data = {
            "structureCode": structureCode,
            "structureName": structureName,
            "delimiter": kwargs.get("delimiter"),
            "sysAccountStructuresDetailDTOS": [
                {
                    # detailCode, detailName, flexValueSetsId, flexValueSetsName 一起填
                    "detailCode": this_code,
                    "detailName": this_name,
                    "flexValueSetsId": flexValueSetsId,
                    "flexValueSetsName": kwargs.get("flexValueSetsName"),
                    "defaultCode": kwargs.get("defaultCode"),
                    "dSql": kwargs.get("dSql"),
                    "defaultName": kwargs.get("defaultName"),
                    "cSql": kwargs.get("cSql")
                }
            ]
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=json.dumps(data))
        # print(f"----------> 基础配置-核算基础-凭证配置-会计结构建立 新增会计结构 接口响应：{response.text}")
        assert response.status_code == 200, "{} 接口响应状态码不是200".format(
            self.__doc__.split("\n")[1].strip("\t"))
        return response.json()

    def LINK_SOB_SECONDARYSEGMENT(
            self,
            serverIp,
            cookie,
            sobId, secondarySegment:str,
            data=None,
            url="/sys/base/sob/secondarySegment",
            **kwargs):
        """
        基础配置-核算基础-凭证配置-会计结构建立 关联适用账套
        sobId多个时传列表格式，一个时字符串即可
        """
        api_name = "基础配置-核算基础-凭证配置-会计结构建立 关联适用账套"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        if isinstance(sobId, list):
            sobId = '^'.join(sobId)
        data = f'sobId={sobId}&secondarySegment={secondarySegment}'
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data.encode('utf-8'))
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return response.json()

    def ADD_STRUCTURESGROUP(
            self,
            serverIp,
            cookie,
            sobId, structureGroupName, structureGroupCode,
            data=None,
            url="/sys/base/structuresgroup",
            **kwargs):
        api_name = "基础配置-核算基础-凭证配置-辅助核算建立 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)

        data = {
            "sobId": sobId,
            "sobName": kwargs.get("sobName"),
            "structureGroupName": structureGroupName,
            "structureGroupCode": structureGroupCode
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_STRUCTURESGROUP(
            self,
            serverIp,
            cookie,
            data=None,
            url="/sys/base/structuresgroup/list",
            **kwargs):
        """
        查询条件在ride使用"xxx"="yyy"的格式传入即可
        """
        api_name = "基础配置-核算基础-凭证配置-辅助核算建立 分页查询"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        # 原始基础条件
        page = kwargs.get('page') if kwargs.get('page') else '1'
        limit = kwargs.get('limit') if kwargs.get('limit') else '20'
        suite = {'page': page, 'limit': limit}
        kwargs.update(suite)
        dlist = []
        for key, value in kwargs.items():
            dlist.append(str(key) + '=' + str(value))
        data = "&".join(dlist)

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data.encode('utf-8'))
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return response.json()

    def ADD_STRUCTCONF(
            self,
            serverIp,
            cookie,
            id, ids,
            data=None,
            url="/sys/base/structuresgroup/config",
            **kwargs):
        # id：辅助核算id
        # ids:核算组合id,多个id用^连接ids: id1^id2，或list
        api_name = "基础配置-核算基础-凭证配置-辅助核算建立-配置 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        if isinstance(ids, list):
            ids = '^'.join(ids)
        data = {
            "id": id,
            "ids": ids
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def ADD_SPECIALGLTYPE(
            self,
            serverIp,
            cookie,
            sobId, sobCode, accountType, glCode, glName,
            data=None,
            url="/sys/base/specialgltype",
            **kwargs):

        api_name = "基础配置-核算基础-SPA相关配置-特殊总账类型 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)

        data = {
            "sobCode": sobCode,
            "sobName": kwargs.get("sobName"),
            "accountType": accountType,
            "glCode": glCode,
            "glName": glName,
            "sobId": sobId
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def QUERY_SPECIALGLTYPE(
            self,
            serverIp,
            cookie,
            data=None,
            url="/sys/base/specialgltype/page",
            **kwargs):
        """
        查询条件在ride使用"xxx"="yyy"的格式传入即可
        """
        api_name = "基础配置-核算基础-SPA相关配置-特殊总账类型 分页查询"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        # 原始基础条件
        page = kwargs.get('page') if kwargs.get('page') else '1'
        limit = kwargs.get('limit') if kwargs.get('limit') else '20'
        suite = {'page': page, 'limit': limit}
        kwargs.update(suite)
        dlist = []
        for key, value in kwargs.items():
            dlist.append(key + '=' + value)
        data = "&".join(dlist)

        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            data=data.encode('utf-8'))
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        # print(f"----------> {api_name} 接口响应：{response.text}")
        return response.json()
    def ADD_SAPSGL(
            self,
            serverIp,
            cookie,
            sobCode, coaAccountType, sgl, controlAccountId, targetAccountId, coaSchemeId,
            data=None,
            url="/sys/base/sapsgl",
            **kwargs):
        api_name = "基础配置-核算基础-SPA相关配置-特殊总账标识 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)

        data = {
            "sobCode": sobCode,
            "sobName": kwargs.get("sobName"),
            "coaAccountType": coaAccountType,
            "sgl": sgl,
            "sglName": kwargs.get("sglName"),
            "controlAccountId": controlAccountId,
            "controlAccountName": kwargs.get("controlAccountName"),
            "targetAccountId": targetAccountId,
            "targetAccountName": kwargs.get("targetAccountName"),
            "validityFlag": kwargs.get("validityFlag"),
            "coaSchemeId": coaSchemeId,
            "coaSchemeName": kwargs.get("coaSchemeName")
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()

    def ADD_POSTINGKEY(
            self,
            serverIp,
            cookie,
            sobId, sobCode, sobName,
            data=None,
            url="/sys/base/postingkey",
            **kwargs):
        api_name = "基础配置-核算基础-SPA相关配置-记账代码 新增"
        request_url = serverIp + url
        headers = {
            "Cookie": cookie,
            "Content-Type": "application/json;charset=UTF-8"
        }
        if data:
            if isinstance(data, str):
                kwargs.update(eval(data))
            if isinstance(data, dict):
                kwargs.update(data)
        data = {
            "sobId": sobId,
            "sobCode": sobCode,
            "sobName": sobName,
            "postingKeyCode": kwargs.get("postingKeyCode"),
            "postingKeyName": kwargs.get("postingKeyName"),
            "debitLenderCode": random.choice(['DEBIT', 'CREDIT']),
            "accountType": random.choice(['A', 'D', 'K', 'S']),
            "glFlag": random.choice(['0', '1'])
        }
        response = requests.post(
            timeout=timeout,
            url=request_url,
            headers=headers,
            json=data)
        # print(f"----------> {api_name} 接口响应：{response.text}")
        assert response.status_code == 200, f"{api_name} 接口断言失败，接口响应：{response.json()}"
        return response.json()


class baseData(ZTCCLOUD):
    # 创建公司部门岗位员工
    def create_comp_dept(self, confList, value, **kwargs):
        """
        创建公司部门岗位员工
        self：报账方法类的实例对象
        confList：配置文件对象，dict
        value：界面值
        base_url：基础url
        kwargs: namedflag每笔数据的标记；
        """
        numdas = 1
        try:
            kwargs.update(confList)
            kwargs.update(value)
            base_url = kwargs.get('base_url')
            cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
            num_comp = abs(int(kwargs.get('comp_num')) if kwargs.get('comp_num') else 0)  # 公司数
            num_dept = abs(int(kwargs.get('dept_num')) if kwargs.get('dept_num') else 0)  # 每个公司下的部门数
            num_post = abs(int(kwargs.get('post_num')) if kwargs.get('post_num') else 0)  # 每个部门下的岗位数
            num_emp = abs(int(kwargs.get('emp_num')) if kwargs.get('emp_num') else 0)  # 每个岗位下的正岗员工数(新建员工、加到岗位下)
            namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
            root = value['root'].strip("\n").strip(" ").strip("\t")
        except:
            sg.popup("字段值异常，公司部门数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；", title='错误')
            return
        comp_num = int(self.random_character(length='5'))  # 公司级随机数

        # 公司部分
        dType = 0  # 公司
        if not root:
            pid_c = self.QUERY_DEPT_FINDCHILDRENBYIDANDVALIDITY(base_url, cookie)  # 集团id
        else:
            root_list = root.split('/')
            result = self.QUERY_DEPT_FINDPARENTBYNAMEANDVALIDITY(base_url, cookie, name=root_list[0])  # 公司的pid
            pid_c = self.GET_DICT(result, 'id', name=root)

            if not pid_c:
                if sg.popup_yes_no("按根节点信息没找到对应的公司\n是否直接建到集团节点下?", title='选择') == "Yes":
                    pid_c = self.QUERY_DEPT_FINDCHILDRENBYIDANDVALIDITY(base_url, cookie)  # 集团id
                else:
                    return
        deptFid = f'/{pid_c} ^ 4'
        companyId_list = []  # 公司id列表
        level_c = 1
        for num_c in range(1, num_comp + 1):

            companyCode = f"{namedFlag}companyCode{str(comp_num)}_{str(num_c)}"
            companyName = f"{namedFlag}companyName{str(comp_num)}_{str(num_c)}"
            result = self.ADD_COMMON_DEPT(base_url, cookie, dType, companyCode, companyName, f'LV{str(level_c)}', pid_c,
                                          deptFid)
            while '级别配置有误' in result.get('msg'):
                level_c += 1
                result = self.ADD_COMMON_DEPT(base_url, cookie, dType, companyCode, companyName, f'LV{str(level_c)}',
                                              pid_c, deptFid)
                if result.get('msg') == "success" or level_c > 10:
                    break

            id = self.GET_DICT(result, 'id')
            companyId_list.append(id)
            if num_dept <= 0:
                numdas += 1
                list_base_num[0] += 1

        permanentLand_temp = self.QUERY_CITY_INFO(base_url, cookie)

        # 部门部分
        dType = 1  # 部门
        level_d = 1
        dept_num = int(self.random_character(length='5'))  # 部门级随机数
        postId_temp = ""  # 缓存一次岗位id，用于下一个岗位员工的兼岗

        for index, pid in enumerate(companyId_list):
            for num_d in range(1, num_dept + 1):
                deptFid = f'/{pid_c}^4/{pid}^0'
                deptName = f"{namedFlag}部门名称{str(comp_num)}_{str(num_d)}"
                deptCode = f"{namedFlag}deptCode{str(comp_num)}_{str(num_d)}"
                result = self.ADD_COMMON_DEPT(base_url, cookie, dType, deptCode, deptName, f'LV{str(level_d)}', pid,
                                              deptFid)
                while '级别配置有误' in result.get('msg'):
                    level_c += 1
                    result = self.ADD_COMMON_DEPT(base_url, cookie, dType, deptCode, deptName, f'LV{str(level_d)}', pid,
                                                  deptFid)
                    if result.get('msg') == "success" or level_d < 10:
                        break
                deptId = self.GET_DICT(result, 'id')
                if not num_post:
                    numdas += 1
                    list_base_num[0] += 1

                # 岗位部分
                pType = 2  # 岗位
                level_p = 1
                post_num = int(self.random_character(length='5'))  # 岗位级随机数

                for num_p in range(1, num_post + 1):
                    postFid = f"/{pid_c}^4/{pid}^0/{deptId}^1/"
                    postName = f"{namedFlag}岗位名称{str(dept_num)}_{str(num_p)}"
                    postCode = f"{namedFlag}postCode{str(dept_num)}_{str(num_p)}"
                    result = self.ADD_COMMON_DEPT(base_url, cookie, pType, postCode, postName, f'LV{str(level_p)}',
                                                  deptId,
                                                  postFid)
                    while '级别配置有误' in result.get('msg'):
                        level_p += 1
                        result = self.ADD_COMMON_DEPT(base_url, cookie, pType, postCode, postName, f'LV{str(level_p)}',
                                                      pid, postFid)
                        if result.get('msg') == "success" or level_p < 10:
                            break
                    if not num_emp:
                        numdas += 1
                        list_base_num[0] += 1
                    postId = self.GET_DICT(result, 'id')  # 岗位id
                    postId_temp_list = []
                    if postId_temp:
                        postId_temp_list = [postId_temp]
                    for num_e in range(1, num_emp + 1):
                        random_num = self.random_character(char_type="str", length="8")
                        empCode = f"{namedFlag}C_{str(random_num)}"
                        empName = f"{namedFlag}员工名称{str(random_num)}"
                        result = self.ADD_COMMON_EMP(base_url, cookie, empNo=empCode, realName_zh_CN=empName,
                                                     permanentLand=permanentLand_temp, mainPost=postId, haveAccount='Y',
                                                     companyId=pid, accessoryPost=postId_temp_list)
                        if result.get('msg') == 'success':
                            numdas += 1
                            list_base_num[0] += 1
                        elif result.get("msg") == "字段长度超过限制":
                            sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff',
                                                   auto_close_duration=5)
                            return "字段长度超出限制，请控制下长度[末尾特征标记]"
                    postId_temp = postId  # 缓存一次岗位id，用于下一个岗位员工的兼岗

                    post_num += 1
                dept_num += 1
            comp_num += 1

        return " 执行完成! "

    # 创建客商
    def create_vendors(self, confList, value, **kwargs):
        """
        创建客商
        self：报账方法类的实例对象
        confList：配置文件对象，dict
        value：界面值
        base_url：基础url
        kwargs: namedflag每笔数据的标记；
        """
        msg = ''
        try:
            kwargs.update(confList)
            kwargs.update(value)
            base_url = kwargs.get('base_url')
            cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
            num_makeing = abs(int(kwargs.get('num_vendors')) if kwargs.get('num_vendors') else 0)  # 创建客商数
            num2_makeing = abs(int(kwargs.get('num_vendors_comp')) if kwargs.get('num_vendors_comp') else 0)  # 每个客商关联科目数
            if num_makeing <= 0:
                return
            namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
        except:
            sg.popup_quick_message("字段值异常，供应商数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                   background_color='#0099ff', auto_close_duration=5)
            return
        bankList = self.FINDDATAS_SplitWord(base_url, cookie, limit=5)
        # 获取核算主体列表
        result09 = {}
        if num2_makeing:
            result09 = self.FINDDATA(base_url, cookie, f'page=1&limit={num2_makeing*2}&orderField=&order=&lovKey=sysFuLeMode.findNameAndCode&SOB_ID=')
        leId_list = self.GET_VALUE_FROM_JSON_DICT(result09, 'ID')
        coaSchemeId_list = self.GET_VALUE_FROM_JSON_DICT(result09, 'COA_SCHEME_ID')
        leId_coaId_list = [list(k) for k in zip(leId_list, coaSchemeId_list)]
        if len(leId_list) < num2_makeing:
            sg.popup_quick_message("该环境核算主体数量至少应比每个客商分配科目的数量多",
                                   background_color='#0099ff', auto_close_duration=5)
            return '该环境核算主体数量至少应比每个客商分配科目的数量多'
        numdas, numdas2 = 1, 1
        for num_v in range(1, num_makeing + 1):
            leId_coaId_list = random.sample(leId_coaId_list, k=num2_makeing)
            # # 随机拿几个核算主体
            # if leId_coaId_list:
            #     lenlecoaId = 10
            #     if len(leId_coaId_list) < 10:
            #         lenlecoaId = len(leId_coaId_list)
            #     leId_coaId_list = random.sample(leId_coaId_list, k=random.randint(1, lenlecoaId))
            leId_list_choiced = [i[0] for i in leId_coaId_list]
            coaSchemeId_list_choiced = [i[1] for i in leId_coaId_list]

            random_num = f'{random.randint(int(1e+9), int(1e+10))}'  # 随机数10位
            vendorCode = f"{namedFlag}{str(random_num)}"
            if namedFlag:
                vendorName = f"{namedFlag}客商名称{str(random_num)}"
            else:
                vendorName = f"客商名称{str(random_num)}"
            taxNumber = f"{namedFlag}taxNum{str(random_num)}_{str(num_v)}"
            # 是否全局客商
            globalVendor = str(random.randint(0, 1))
            result = self.ADD_VENDORS(base_url, cookie, vendorCode, vendorName, taxNumber,
                                      data=random.choice(bankList), globalVendor=globalVendor,
                                      vendorsCompany=leId_list_choiced)
            if result.get("msg") == "字段长度超过限制":
                sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff', auto_close_duration=5)
                break
            if result.get('msg') == 'success':
                list_base_num[1] += 1
                vendorId = self.GET_DICT(self.SIMPLE_QUERY_VENDORS(base_url, cookie, vendorCode=vendorCode), 'id')
                for key, leId in enumerate(leId_list_choiced):
                    accountList = self.GET_COAINFO_MUST(base_url, cookie, coaSchemeId=coaSchemeId_list_choiced[key])
                    k = random.randint(0, len(accountList)-1)
                    if globalVendor == '0':  # 如果是全局
                        resultoo = self.ADD_VENDORSCOMPANY(base_url, cookie, leId=leId, accountId=accountList[k][0], accountErpCode=accountList[k][2],
                                            accountErpName=accountList[k][1], vendorId=vendorId)
                    else:
                        resultr = self.QUERY_VENDORSCOMPANY(base_url, cookie, vendorId=vendorId, flagValid='0', leId=leId)
                        Id = self.GET_DICT(resultr, 'id')
                        initRowIndex = self.GET_DICT(resultr, 'initRowIndex')
                        resultoo = self.ADD_VENDORSCOMPANY(base_url, cookie, leId=leId, accountId=accountList[k][0],
                                                accountErpCode=accountList[k][2], accountErpName=accountList[k][1],
                                                vendorId=vendorId, id=Id, initRowIndex=initRowIndex, post='put')
                    if resultoo.get('msg') == 'success':
                        list_base_num_more[8] += 1
                        numdas2 += 1
                    else:
                        msg_ = resultoo.get('msg')
                        if msg_:
                            msg += msg_ + '\n'
            else:
                msg_ = result.get('msg')
                if msg_:
                    msg += msg_ + '\n'

            # sg.popup_quick_message(f"完成{list_base[0]}：{numdas}", background_color="green")
            # progress_bar(numdas, num_makeing)

            numdas += 1

    # 创建临时供应商
    def create_temp_vendors(self, confList, value, **kwargs):
        """
        创建客商
        self：报账方法类的实例对象
        confList：配置文件对象，dict
        value：界面值
        base_url：基础url
        kwargs: namedflag每笔数据的标记；
        """
        try:
            kwargs.update(confList)
            kwargs.update(value)
            cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
            num_makeing = abs(int(kwargs.get('num_temp_vendors')) if kwargs.get('num_temp_vendors') else 0)  # 创建临时客商数
            if num_makeing <= 0:
                return
            namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
        except:
            sg.popup_quick_message("字段值异常，临时供应商数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                   background_color='#0099ff', auto_close_duration=5)
            return
        bankList = self.FINDDATAS_SplitWord(base_url, cookie, limit=5)
        numdas = 1
        vendors_fs = int(self.random_character(length='5'))  # 高并发专用随机数
        for num_v in range(1, num_makeing + 1):
            random_num = f'{random.randint(int(1e+9), int(1e+10))}'  # 随机数10位
            bankAccountName = f"{namedFlag}{str(random_num)}_{vendors_fs}"
            if namedFlag:
                vendorName = f"{namedFlag}临时供应商{str(random_num)}"
            else:
                vendorName = f"临时供应商{str(random_num)}"
            bankAccount = f"{namedFlag}bankAccount{str(random_num)}"
            # ${vendorName}    ${bankAccountName}    ${bankAccount}    2
            result = self.ADD_TEMP_VENDORS(base_url, cookie, vendorName, bankAccountName, bankAccount,
                                           data=bankList[random.randint(0, len(bankList) - 1)])
            if result.get("msg") == "字段长度超过限制":
                sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff', auto_close_duration=5)
                break
            list_base_num[2] += 1

            numdas += 1

    def progress_bar(self, num, count):
        text, text2 = "", ""
        if num == 1:
            text = "执行开始"
        if num == count:
            text2 = "执行完成\n"
        num_now = num * 100 / count
        if (int(num_now) % 10 == 0) or (num == count) or num == 1:
            print(f"{text}" + "->>" + "|" + str(
                int(num_now)) + "%" + f"\r{text2}", end="", flush=True)

    # 创建项目
    def create_project(self, confList, value, **kwargs):
        """
        创建项目
        self：报账方法类的实例对象
        confList：配置文件对象，dict
        value：界面值
        base_url：基础url
        kwargs: namedflag每笔数据的标记；
        """
        try:
            kwargs.update(confList)
            kwargs.update(value)
            cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
            num_makeing = abs(int(kwargs.get('num_project')) if kwargs.get('num_project') else 0)  # 创建客商数
            if num_makeing <= 0:
                return
            namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
        except:
            sg.popup_quick_message("字段值异常，项目数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                   background_color='#0099ff', auto_close_duration=5)
            return
        bankList = self.FINDDATAS_RoleMode(base_url, cookie, limit=10)
        bankList = self.GET_VALUE_FROM_JSON_DICT(bankList, "ID")

        fuList = self.FINDDATAS_FuLeMulti(base_url, cookie, limit=10)
        fuList = self.GET_VALUE_FROM_JSON_DICT(fuList, "ID")

        numdas = 1
        for num_v in range(1, num_makeing + 1):
            random_num = f'{random.randint(int(1e+9), int(1e+10))}'  # 随机数10位
            project_code = f"{namedFlag}{str(random_num)}"
            if namedFlag:
                project_name = f"{namedFlag}项目名称{str(random_num)}"
            else:
                project_name = f"项目名称{str(random_num)}"
            result = self.ADD_PROJECT(base_url, cookie, project_code.replace("_", ""), project_name.replace("_", ""),
                                          principals=bankList[random.randint(1, len(bankList)-1)],
                                          directors=bankList[random.randint(1, len(bankList)-1)],
                                          fuId=fuList[random.randint(0, len(bankList)-1)])
            if result.get("msg") == "字段长度超过限制":
                sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff', auto_close_duration=5)
                break
            list_base_num[3] += 1

            numdas += 1

    # 创建维度
    def create_flexvaluesets(self, confList, value, **kwargs):
        """
        创建维度
        self：报账方法类的实例对象
        confList：配置文件对象，dict
        value：界面值
        base_url：基础url
        kwargs: namedflag每笔数据的标记；
        """
        try:
            kwargs.update(confList)
            kwargs.update(value)
            cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
            num_makeing = abs(int(kwargs.get('num_flexvaluesets')) if kwargs.get('num_flexvaluesets') else 0)  # 创建维度数
            num2_makeing = abs(int(kwargs.get('num_flexvaluesetscustom')) if kwargs.get('num_flexvaluesetscustom') else 0)  # 创建成员数
            if num_makeing <= 0:
                return
            namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
        except:
            sg.popup_quick_message("字段值异常，项目数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                   background_color='#0099ff', auto_close_duration=5)
            return
        numdas = 1
        code_list = []
        for num_v in range(1, num_makeing + 1):
            random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数10位
            this_code = f"{namedFlag}flex^_^{str(random_num)}"
            if namedFlag:
                this_name = f"{namedFlag}维度名称{str(random_num)}"
            else:
                this_name = f"维度名称{str(random_num)}"
            result = self.ADD_FLEXVALUESETS(base_url, cookie, this_code, this_name)
            if result.get("msg") == "字段长度超过限制":
                sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff', auto_close_duration=5)
                break
            if result.get('msg') == 'success':
                code_list.append(this_code)
            if num2_makeing <= 0:
                list_base_num[4] += 1
                numdas += 1
        if num2_makeing <= 0:
            return
        for code in code_list:
            list_id = self.QUERY_FLEXVALUESETS(base_url, cookie, keyword=code)
            if not len(list_id):
                break
            for ss in range(1, num2_makeing + 1):
                random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数10位
                this_code = f"{namedFlag}flex-_-{str(random_num)}"
                if namedFlag:
                    this_name = f"{namedFlag}维度成员名称{str(random_num)}"
                else:
                    this_name = f"维度成员名称{str(random_num)}"
                result = self.ADD_FLEXVALUESETS_CUSTOM(base_url, cookie, this_code, this_name, list_id[0])
                if result.get("msg") == "字段长度超过限制":
                    sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff', auto_close_duration=5)
                    break
                list_base_num[4] += 1
                numdas += 1

    # 创建科目体系
    def create_coaaccountscheme(self, confList, value, **kwargs):
        """
        创建科目体系及体系下的会计科目
        self：报账方法类的实例对象
        confList：配置文件对象，dict
        value：界面值
        base_url：基础url
        kwargs: namedflag每笔数据的标记；
        """
        try:
            kwargs.update(confList)
            kwargs.update(value)
            cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
            num_makeing = abs(int(kwargs.get('num_coaaccountscheme')) if kwargs.get('num_coaaccountscheme') else 0)  # 创建科目体系数
            num2_makeing = abs(int(kwargs.get('num_coaaccounterp')) if int(kwargs.get('num_coaaccounterp')) else 1)  # 创建会计科目数
            if num_makeing <= 0:
                return
            namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
        except:
            sg.popup_quick_message("字段值异常，项目数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                   background_color='#0099ff', auto_close_duration=5)
            return
        numdas = 1
        code_list = []
        for num_v in range(1, num_makeing + 1):
            random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数10位
            this_code = f"{namedFlag}scheme{str(random_num)}"
            if namedFlag:
                this_name = f"{namedFlag}科目名称{str(random_num)}"
            else:
                this_name = f"科目名称{str(random_num)}"
            result = self.ADD_SCHEME(base_url, cookie, this_code, this_name)
            if result.get("msg") == "字段长度超过限制":
                sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff', auto_close_duration=5)
                break
            if result.get('msg') == 'success':
                code_list.append(this_code)
            if num2_makeing <= 0:
                list_base_num[5] += 1
                numdas += 1
        if num2_makeing <= 0:
            return
        for code in code_list:
            list_id = self.QUERY_SCHEME(base_url, cookie, keyword=code)
            if not len(list_id):
                break
            for ss in range(1, num2_makeing + 1):
                random_num = f'{random.randint(int(1e+9), int(1e+10))}'  # 随机数10位
                this_code = f"{namedFlag}Coa{str(random_num)}"
                this_name = f"{namedFlag}会计科目名称{str(random_num)}"
                result = self.ADD_COA(base_url, cookie, this_code, this_name, list_id[0])
                if result.get("msg") == "字段长度超过限制":
                    sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff', auto_close_duration=5)
                    break
                list_base_num[5] += 1
                numdas += 1

    # 创建汇率方案
    def create_exchangerate(self, confList, value, **kwargs):
        """
        创建汇率方案下的汇率
        self：报账方法类的实例对象
        confList：配置文件对象，dict
        value：界面值
        base_url：基础url
        kwargs: namedflag每笔数据的标记；
        """
        try:
            kwargs.update(confList)
            kwargs.update(value)
            cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
            num_makeing = abs(int(kwargs.get('num_exchangerate')) if kwargs.get('num_exchangerate') else 0)  # 创建汇率方案数
            num2_makeing = abs(int(kwargs.get('num_exchangerate_conf')) if kwargs.get('num_exchangerate_conf') else 0)  # 创建汇率数
            if num_makeing <= 0:
                return
            namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
        except:
            sg.popup_quick_message("字段值异常，项目数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                   background_color='#0099ff', auto_close_duration=5)
            return
        result1 = self.FINDDATA(base_url, cookie, data='page=1&limit=50&orderField=&order=&lovKey=sysCurrencyMode.findNameAndCodeSingle&hasCount=N')
        dictCode_list = self.GET_VALUE_FROM_JSON_DICT(result1, 'ID')  # 币种列表
        CURRENCY_CODE_list = self.GET_VALUE_FROM_JSON_DICT(result1, 'CURRENCY_CODE')  # 币种列表
        numdas = 1
        code_list = []
        for num_v in range(1, num_makeing + 1):
            random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数8位
            this_code = f"{namedFlag}rate{str(random_num)}"
            if namedFlag:
                this_name = f"{namedFlag}汇率方案名称{str(random_num)}"
            else:
                this_name = f"汇率方案名称{str(random_num)}"
            # 挑一个币种
            curr_id = dictCode_list[random.randint(0, len(dictCode_list)-1)]
            result = self.ADD_EXCHANGERATE(base_url, cookie, this_code, this_name, curr_id)
            if result.get("msg") == "字段长度超过限制":
                sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff', auto_close_duration=5)
                break
            if result.get('msg') == 'success':
                code_list.append(this_name)
            if num2_makeing <= 0:
                list_base_num[6] += 1
                numdas += 1

        if num2_makeing <= 0:
            return

        for code in code_list:
            list_id = self.QUERY_EXCHANGERATE(base_url, cookie, rateSchemeName=code)
            if not len(list_id):
                break
            rateDate = datetime.date.today()  # 获取「今天」
            saas = 0
            while saas < num2_makeing:
                if rateDate.month == 12:  # 下一月
                    rateDate = rateDate.replace(year=rateDate.year + 1)
                    rateDate = rateDate.replace(month=rateDate.month - 11)
                else:
                    rateDate = rateDate.replace(month=rateDate.month + 1)
                if len(CURRENCY_CODE_list):
                    for currcode in CURRENCY_CODE_list:
                        result = self.ADD_RATECON(base_url, cookie, list_id, rateDate=rateDate.strftime("%Y-%m-%d"), scalerCurrencyCode=currcode)
                        if result.get("msg") == "字段长度超过限制":
                            sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff', auto_close_duration=5)
                            break
                        saas += 1
                        list_base_num[6] += 1
                        numdas += 1
                        if not saas < num2_makeing:
                            break
                else:
                    result = self.ADD_RATECON(base_url, cookie, list_id, rateDate=rateDate.strftime("%Y-%m-%d"))
                    saas += 1
                    list_base_num[6] += 1
                    numdas += 1
                    if result.get("msg") == "字段长度超过限制":
                        sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff', auto_close_duration=5)
                        break

    # 创建账套-核算主体-成本中心
    def create_sob(self, confList, value, **kwargs):
        """
        创建账套-核算主体-成本中心
        confList：配置文件对象，dict
        value：界面值
        kwargs: namedflag每笔数据的标记；
        """
        method_name = "创建账套-核算主体-成本中心"
        msg = ''
        try:
            try:
                kwargs.update(confList)
                kwargs.update(value)
                base_url = kwargs.get('base_url')
                cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
                num_makeing = abs(int(kwargs.get('num_sob')) if kwargs.get('num_sob') else 0)  # 创建账套数
                num2_makeing = abs(int(kwargs.get('num_fule')) if kwargs.get('num_fule') else 0)  # 创建核算主体数
                num3_makeing = abs(int(kwargs.get('num_fulecc')) if kwargs.get('num_fulecc') else 0)  # 创建成本中心数
                if num_makeing <= 0:
                    return
                namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
            except:
                sg.popup_quick_message("字段值异常，造数数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                       background_color='#0099ff', auto_close_duration=5)
                return
            # 获取币种列表
            result1 = self.FINDDATA(base_url, cookie,
                                    data='page=1&limit=50&orderField=&order=&lovKey=sysCurrencyMode.findNameAndCodeSingle&hasCount=N')
            CURRENCY_CODE_list = self.GET_VALUE_FROM_JSON_DICT(result1, 'CURRENCY_CODE')  # 币种列表
            # 获取汇率方案exchangerate和本位币currencyCode
            concurrent_ = self.FINDDATA(base_url, cookie,
                                        data="page=1&limit=50&orderField=&order=&lovKey=sysExchangeRateMode.findNameAndCode&hasCount=N")
            list_BASIC_CURRENCY_ID = self.GET_VALUE_FROM_JSON_DICT(concurrent_, 'ID')
            list_BASIC_CURRENCY_CODE = self.GET_VALUE_FROM_JSON_DICT(concurrent_,
                                                                     'BASIC_CURRENCY_CODE')  # BASIC_CURRENCY_CODE
            ID_CODE_LIST = [list(t) for t in zip(list_BASIC_CURRENCY_ID, list_BASIC_CURRENCY_CODE)]
            # 获取科目体系coaaccountscheme
            result = self.FINDDATA(base_url, cookie,
                                   "page=1&limit=1000&orderField=&order=&lovKey=sysCoaAccountSchemeMode.findNameAndCode&hasCount=N")
            list_coaaccountscheme = self.GET_VALUE_FROM_JSON_DICT(result, 'ID')
            # # 给所有没配置科目的科目体系加餐
            # random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数8位
            # for coaaccountscheme in list_coaaccountscheme:
            #     if not self.QUERY_COA(base_url, cookie, accountSchemeId=coaaccountscheme).get('page').get('totalCount'):
            #         this_Coacode = f"{namedFlag}Coa{str(random_num)}"
            #         this_Coaname = f"{namedFlag}会计科目名称{str(random_num)}"
            #         self.ADD_COA(base_url, cookie, this_Coacode, this_Coaname, coaaccountscheme)
            # print("加餐结束")
            # return
            numdas = 1
            sob_id_list = []
            for num_v in range(1, num_makeing + 1):
                random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数8位
                this_code = f"{namedFlag}sob{str(random_num)}"
                this_name = f"{namedFlag}账套名称{str(random_num)}"
                # 挑一个币种
                curr_code = random.choice(CURRENCY_CODE_list)
                # 挑一个汇率方案exchangerate和本位币
                saas = random.choice(ID_CODE_LIST)
                exchangerate = saas[0]  # 汇率方案
                currencyCode = saas[-1]  # 本位币
                # 挑一个科目体系
                coaaccountscheme = random.choice(list_coaaccountscheme)
                if not self.QUERY_COA(base_url, cookie, accountSchemeId=coaaccountscheme).get('page').get('totalCount'):
                    this_Coacode = f"{namedFlag}Coa{str(random_num)}"
                    this_Coaname = f"{namedFlag}会计科目名称{str(random_num)}"
                    self.ADD_COA(base_url, cookie, this_Coacode, this_Coaname, coaaccountscheme)
                num_this = 1
                while True:
                    result = self.ADD_SOB(base_url, cookie, this_code, this_name, coaaccountscheme, exchangerate,
                                          currencyCode=currencyCode, useCurrencyCode=curr_code)
                    if result.get("msg") == "字段长度超过限制":
                        sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff',
                                               auto_close_duration=5)
                        return "字段长度超出限制，请控制下长度[末尾特征标记]"
                    if result.get('msg') == 'success':
                        sob_id = self.QUERY_SOB(base_url, cookie, sobCode=this_code).get('page').get('list')[0].get(
                            'id')
                        sob_id_list.append(sob_id)
                        break
                    else:
                        if num_this >= 3:
                            msg_ = result.get('msg')
                            if msg_:
                                msg += msg_ + '\n'
                            break
                        else:
                            num_this += 1
                            continue
                if num_this >= 3:
                    break

                if num2_makeing <= 0:
                    list_base_num[7] += 1
                    numdas += 1

            if num2_makeing <= 0:
                return

            # 新建核算主体
            for sobId in sob_id_list:
                for num in range(num2_makeing):
                    random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数8位
                    this_code = f"{namedFlag}fule{str(random_num)}"
                    this_name = f"{namedFlag}核算主体名称{str(random_num)}"
                    result = self.ADD_FULECC(base_url, cookie, this_code, this_name, sobId)
                    fuleccbzx_id = self.GET_DICT(result, 'id', code=this_code)
                    if num3_makeing == 0 and fuleccbzx_id:
                        list_base_num[7] += 1
                        numdas += 1
                    if not fuleccbzx_id:
                        continue
                    for numw in range(num3_makeing):
                        random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数8位
                        this_code = f"{namedFlag}fuleCC{str(random_num)}"
                        if namedFlag:
                            this_name = f"{namedFlag}成本中心名称{str(random_num)}"
                        else:
                            this_name = f"成本中心名称{str(random_num)}"
                        result = self.ADD_FULECC(base_url, cookie, this_code, this_name,
                                                 affiliatedFuleId=fuleccbzx_id,
                                                 pid=fuleccbzx_id, fuType=1)
                        if result.get('msg') == 'success':
                            list_base_num[7] += 1
                            numdas += 1
        except Exception as e:
            traceback.print_exc()
            return msg + '\n' + str(e)

    # 创建税种税率
    def create_taxrate(self, confList, value, **kwargs):
        """
        confList：配置文件对象，dict
        value：界面值
        kwargs: namedflag每笔数据的标记；
        """
        method_name = "创建税种税率"
        msg = ''
        try:
            try:
                kwargs.update(confList)
                kwargs.update(value)
                base_url = kwargs.get('base_url')
                cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
                num_makeing = abs(int(kwargs.get('num_taxrate')) if kwargs.get('num_taxrate') else 0)  # 创建税种税率数
                if num_makeing <= 0:
                    return
                namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
            except:
                sg.popup_quick_message("字段值异常，造数数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                       background_color='#0099ff', auto_close_duration=5)
                return
            # 获取票据类型
            result1 = self.BILL_TYPE_INVOICE_TAXRATE(base_url, cookie)
            list_bill_type_invoice_code = self.GET_VALUE_FROM_JSON_DICT(result1, 'dictCode')  # 编码
            # 获取税率种类
            taxrate = self.TAX_RATE_TAXRATE(base_url, cookie)
            list_taxrate_dictCode = self.GET_VALUE_FROM_JSON_DICT(taxrate, 'dictCode')
            numdas = 1
            id_list = []
            for billTypeInvoice in list_bill_type_invoice_code:
                random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数8位
                this_code = f"{namedFlag}taxrate{str(random_num)}"
                result = self.ADD_TAXRATE(base_url, cookie, this_code, billTypeInvoice=billTypeInvoice, pid='0')
                if result.get("msg") == "字段长度超过限制":
                    sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff', auto_close_duration=5)
                    return "字段长度超出限制，请控制下长度[末尾特征标记]"
                if result.get('msg') == 'success':
                    id_this = self.QUERY_TAXRATE(base_url, cookie, taxCode=this_code)
                    id_list.append([id_this, billTypeInvoice])
                    for taxrate in list_taxrate_dictCode:
                        random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数8位
                        this_code = f"{namedFlag}taxrate{str(random_num)}"
                        result = self.ADD_TAXRATE(base_url, cookie, this_code, billTypeInvoice=billTypeInvoice,
                                                  pid=id_this, taxValue=taxrate)
                        if result.get("msg") == "字段长度超过限制":
                            sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff',
                                                   auto_close_duration=5)
                            return "字段长度超出限制，请控制下长度[末尾特征标记]"
                        if result.get('msg') == 'success':
                            list_base_num[8] += 1
                            if numdas >= num_makeing:
                                return f"{method_name}造数据完成"

                            numdas += 1
                        else:
                            msg_ = result.get('msg')
                            if msg_:
                                msg += msg_ + '\n'
                else:
                    msg_ = result.get('msg')
                    if msg_:
                        msg += msg_ + '\n'
            return f"{method_name}造数据的cross组合已用完，新增{numdas}条数据"
        except Exception as e:
            traceback.print_exc()
            return msg + '\n' + str(e)

    # 公司账户
    def create_companybank(self, confList, value, **kwargs):
        """
                confList：配置文件对象，dict
                value：界面值
                kwargs: namedflag每笔数据的标记；
                """
        method_name = "创建公司账户"
        msg = ''
        numdas = 1
        numdas_comp2bank = 0  # 银行&票据科目设置的数量
        try:
            try:
                kwargs.update(confList)
                kwargs.update(value)
                base_url = kwargs.get('base_url')
                cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
                num_makeing = abs(int(kwargs.get('num_companybank')) if kwargs.get('num_companybank') else 0)  # 创建公司账户数
                num2_makeing = abs(int(kwargs.get('num_comp2bank')) if kwargs.get('num_comp2bank') else 0)  # 银行/票据科目配置数量
                if num_makeing <= 0 or num_makeing < num2_makeing:
                    sg.popup_quick_message("创建公司账户数不能小于银行/票据科目配置数量",
                                           background_color='#0099ff', auto_close_duration=5)
                    return "创建公司账户数不能小于银行/票据科目配置数量"
                namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
            except:
                sg.popup_quick_message("字段值异常，造数数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                       background_color='#0099ff', auto_close_duration=5)
                return
            # 查核算主体
            result = self.FINDDATA(base_url, cookie, 'page=1&limit=50&orderField=&order=&lovKey=sysFuLeMode.findNameAndCode&hasCount=N')
            fuId_list = self.GET_VALUE_FROM_JSON_DICT(result, 'ID')  # 核算主体id
            sobId_list = self.GET_VALUE_FROM_JSON_DICT(result, 'SOB_ID')  # 账套id
            fuId_sobId = [list(t) for t in zip(fuId_list, sobId_list)]
            # 查开户行
            result = self.FINDDATA(base_url, cookie, 'page=1&limit=50&orderField=&order=&lovKey=sysVendorMode.findBankAndGroupId&hasCount=N')
            subBankId_list = self.GET_VALUE_FROM_JSON_DICT(result, 'ID')
            bankTypeCode_list = self.GET_VALUE_FROM_JSON_DICT(result, 'BANK_TYPE_CODE')
            subBankId_bankTypeCode = [list(t) for t in zip(subBankId_list, bankTypeCode_list)]
            # 查币种
            result = self.FINDDATA(base_url, cookie, 'page=1&limit=50&orderField=&order=&lovKey=sysCurrencyMode.findNameAndCodeMultiple&hasCount=N')
            currencyId_list = self.GET_VALUE_FROM_JSON_DICT(result, 'ID')
            for numas in range(1, num_makeing+1):
                random_num = self.random_character(char_type="str", length="8")
                this_code = f"{namedFlag}compBank{str(random_num)}"
                this_name = f"{namedFlag}公司账户{str(random_num)}"
                # 取用数据
                fuId, sobId = fuId_sobId[random.randint(0, len(fuId_sobId) - 1)]
                subBankId, bankTypeCode = subBankId_bankTypeCode[random.randint(0, len(subBankId_bankTypeCode) - 1)]
                currencyId = currencyId_list[random.randint(0, len(currencyId_list) - 1)]
                bankLevel_times = 0
                while True:
                    bankLevel = f'{random.randint(1000, int(1e+9) - 1)}'  # 优先级
                    result = self.ADD_COMPANYBANK(base_url, cookie, fuId=fuId, sobId=sobId, code=this_code, subBank=subBankId,
                                                  bankTypeCode=bankTypeCode, currencyId=currencyId, bankAccountName=this_name,
                                                  bankAccountNum=this_code, bankLevel=bankLevel)
                    if result.get("msg") != "优先级重复":
                        if result.get("msg") == "字段长度超过限制":
                            sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff', auto_close_duration=5)
                            return "字段长度超出限制，请控制下长度[末尾特征标记]"
                        if result.get('msg') == 'success':
                            list_base_num[9] += 1
                            if numdas_comp2bank < num2_makeing:  # 还没配置完
                                companyBankId = self.GET_DICT(self.QUERY_COMPANYBANK(base_url, cookie, code=this_code), 'id')
                                result2 = self.ADD_COMP2BANK(base_url, cookie, companyBankId=companyBankId,
                                                             bankAccountNum=this_code, leId=fuId, coaSchemeId=sobId, BANK_ACCOUNT_NAME=this_name)
                                if result2.get('msg') == 'success':
                                    list_base_num_more[3] += 1
                                    numdas_comp2bank += 1
                            if numdas >= num_makeing:
                                return f"{method_name}造数据完成"
                            numdas += 1
                        else:
                            msg_ = result.get('msg')
                            if msg_:
                                msg += msg_ + '\n'
                        break
                    elif bankLevel_times > 50:  # 五十次时运不济，终止造数
                        return f"{method_name}造数据的cross组合已用完，新增{numdas}条数据"
                    else:
                        bankLevel_times += 1
            return f"{method_name}造数据的cross组合已用完，新增{numdas}条数据"
        except Exception as e:
            traceback.print_exc()
            return msg + '\n' + str(e)

    # 结算方式
    def create_paymentmode(self, confList, value, **kwargs):
        """
                confList：配置文件对象，dict
                value：界面值
                kwargs: namedflag每笔数据的标记；
                """
        method_name = "创建结算方式"
        msg = ''
        numdas = 1
        try:
            try:
                kwargs.update(confList)
                kwargs.update(value)
                base_url = kwargs.get('base_url')
                cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
                num_makeing = abs(int(kwargs.get('num_paymentmode')) if kwargs.get('num_paymentmode') else 0)  # 创建结算方式数
                if num_makeing <= 0:
                    return
                namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
            except:
                sg.popup_quick_message("字段值异常，造数数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                       background_color='#0099ff', auto_close_duration=5)
                return
            for numas in range(1, num_makeing + 1):
                random_num = self.random_character(char_type="str", length="8")
                this_code = f"{namedFlag}paymentmode{str(random_num)}"
                this_name = f"{namedFlag}结算方式{str(random_num)}"
                num_ads = 0
                while True:
                    bankLevel = f'{random.randint(1, int(1e+5) - 1)}'  # 优先级
                    result = self.ADD_PAYMENTMODE(base_url, cookie, paymentSort=bankLevel, paymentCode=this_code, paymentName_zh_CN=this_name)
                    if result.get("msg") != "优先级重复":
                        if result.get("msg") == "字段长度超过限制":
                            sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff',
                                                   auto_close_duration=5)
                            return "字段长度超出限制，请控制下长度[末尾特征标记]"
                        if result.get('msg') == 'success':
                            list_base_num[10] += 1
                            if numdas >= num_makeing:
                                return f"{method_name}造数据完成"
                            numdas += 1
                        else:
                            msg_ = result.get('msg')
                            if msg_:
                                msg += msg_ + '\n'
                        break
                    elif num_ads > 50:  # 五十次时运不济，终止造数
                        return f"{method_name}造数据的cross组合已用完，新增{numdas}条数据"
                    else:
                        num_ads += 1
            return f"{method_name}造数据的cross组合已用完，新增{numdas}条数据"
        except Exception as e:
            traceback.print_exc()
            return msg + '\n' + str(e)

    # 业务类型
    def create_category(self, confList, value, **kwargs):
        """
                confList：配置文件对象，dict
                value：界面值
                kwargs: namedflag每笔数据的标记；
                """
        method_name = "创建业务类型"
        msg = ''
        numdas = 1
        try:
            try:
                kwargs.update(confList)
                kwargs.update(value)
                base_url = kwargs.get('base_url')
                cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
                num_makeing = abs(int(kwargs.get('num_category')) if kwargs.get('num_category') else 0)  # 创建业务大类数
                num2_makeing = abs(int(kwargs.get('num_small_category')) if kwargs.get('num_small_category') else 0)  # 创建业务小类数
                if num_makeing <= 0:
                    return
                namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
            except:
                sg.popup_quick_message("字段值异常，造数数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                       background_color='#0099ff', auto_close_duration=5)
                return
            for numas in range(1, num_makeing + 1):
                random_num = self.random_character(char_type="str", length="8")
                # random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数8位
                this_code = f"{namedFlag}{str(random_num)}"
                this_name = f"{namedFlag}业务类型{str(random_num)}"
                result = self.ADD_CATEGORY(base_url, cookie, code=this_code, name_zh_CN=this_name, pid='0')
                if result.get("msg") == "字段长度超过限制":
                    sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff',
                                           auto_close_duration=5)
                    return "字段长度超出限制，请控制下长度[末尾特征标记]"
                if result.get('msg') == 'success':
                    if num2_makeing <= 0:
                        list_base_num[11] += 1
                        numdas += 1
                    pid = self.GET_DICT(result, 'id')
                    # 新建业务小类
                    for small in range(1, num2_makeing + 1):
                        random_num = self.random_character(char_type="str", length="8")
                        this_code = f"{namedFlag}{str(random_num)}"
                        this_name = f"{namedFlag}业务小类{str(random_num)}"
                        result = self.ADD_CATEGORY(base_url, cookie, code=this_code, name_zh_CN=this_name, pid=pid, type=1)
                        if result.get('msg') == 'success':
                            list_base_num[11] += 1
                            numdas += 1
                        else:
                            msg_ = result.get('msg')
                            if msg_:
                                msg += msg_ + '\n'
                else:
                    msg_ = result.get('msg')
                    if msg_:
                        msg += msg_ + '\n'
            return msg
        except Exception as e:
            traceback.print_exc()
            return msg + '\n' + str(e)

    # 会计科目配置
    def create_acctsubconf(self, confList, value, **kwargs):
        method_name = "会计科目相关配置"
        msg = ''
        try:
            try:
                kwargs.update(confList)
                kwargs.update(value)
                base_url = kwargs.get('base_url')
                cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
                num_makeing = abs(int(kwargs.get('num_acctsubconf')) if kwargs.get('num_acctsubconf') else 0)  # 创建会计科目相关配置数量
                if num_makeing <= 0:
                    return
                namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
            except:
                sg.popup_quick_message("字段值异常，造数数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                       background_color='#0099ff', auto_close_duration=5)
                return
            # 业务科目配置：
            # 查业务类型大小类所有id
            result = self.QUERY_CATEGORY_TREE(base_url, cookie)
            caregory_list = set(self.GET_VALUE_FROM_JSON_DICT(result, 'id'))
            # 查业务科目类型所有id
            result = self.QUERY_BIZ_ACCOUNT_TYPE(base_url, cookie)
            accountType_list = self.GET_VALUE_FROM_JSON_DICT(result, 'dictCode')


            # 求出要建立多少账套
            num_yewu_kmpz = int(num_makeing/(len(caregory_list)*len(accountType_list))) + 1
            # 税务科目配置：
            # 查税种税率所有id
            result = self.QUERY_TAXRATE_TREE(base_url, cookie)
            taxrate_list = set(self.GET_VALUE_FROM_JSON_DICT(result, 'id'))
            # 进项销项
            accountType2_list = ["ENTRY", "SALES"]
            # 求出要建立多少账套
            num_shuizhong_kmpz = int(num_makeing/(len(taxrate_list)*len(accountType2_list))) + 1
            # 结算科目配置：
            # 查结算方式50条所有id，和结算方式总数
            result = self.QUERY_PAYMENTMODE(base_url, cookie)
            paymentmode_list = self.GET_VALUE_FROM_JSON_DICT(result, 'id')
            transferFundFlag_list = self.GET_VALUE_FROM_JSON_DICT(result, 'transferFundFlag')
            paymentmode_len = self.GET_DICT(result, 'totalCount')
            # 求出要建立多少账套
            num_jiesuan_kmpz = int(num_makeing/int(paymentmode_len)) + 1
            num_sob_makeing = max([num_yewu_kmpz, num_shuizhong_kmpz, num_jiesuan_kmpz])
            sob_id_list = []  # 存放账套id
            coaSchemeId_list = []  # 存放科目体系id

            if num_sob_makeing > 0:
                # 获取币种列表
                result1 = self.FINDDATA(base_url, cookie,
                                        data='page=1&limit=50&orderField=&order=&lovKey=sysCurrencyMode.findNameAndCodeSingle&hasCount=N')
                CURRENCY_CODE_list = self.GET_VALUE_FROM_JSON_DICT(result1, 'CURRENCY_CODE')  # 币种列表
                # 获取汇率方案exchangerate和本位币currencyCode
                concurrent_ = self.FINDDATA(base_url, cookie,
                                            data="page=1&limit=50&orderField=&order=&lovKey=sysExchangeRateMode.findNameAndCode&hasCount=N")
                list_BASIC_CURRENCY_ID = self.GET_VALUE_FROM_JSON_DICT(concurrent_, 'ID')
                list_BASIC_CURRENCY_CODE = self.GET_VALUE_FROM_JSON_DICT(concurrent_, 'BASIC_CURRENCY_CODE')
                ID_CODE_LIST = [list(t) for t in zip(list_BASIC_CURRENCY_ID, list_BASIC_CURRENCY_CODE)]
                # 获取科目体系coaaccountscheme
                result = self.FINDDATA(base_url, cookie,
                                       "page=1&limit=1000&orderField=&order=&lovKey=sysCoaAccountSchemeMode.findNameAndCode&hasCount=N")
                list_coaaccountscheme = self.GET_VALUE_FROM_JSON_DICT(result, 'ID')
                for num_v in range(1, num_sob_makeing + 1):
                    random_num = f'{random.randint(int(1e+7), int(1e+8))}'  # 随机数8位
                    this_code = f"{namedFlag}sob{str(random_num)}"
                    this_name = f"{namedFlag}账套名称{str(random_num)}"
                    # 挑一个币种
                    curr_code = random.choice(CURRENCY_CODE_list)
                    # 挑一个汇率方案exchangerate和本位币
                    saas = random.choice(ID_CODE_LIST)
                    exchangerate = saas[0]  # 汇率方案
                    currencyCode = saas[-1]  # 本位币
                    # 挑一个科目体系
                    coaaccountscheme = random.choice(list_coaaccountscheme)
                    if not self.QUERY_COA(base_url, cookie, accountSchemeId=coaaccountscheme).get('page').get(
                            'totalCount'):
                        this_Coacode = f"{namedFlag}Coa{str(random_num)}"
                        this_Coaname = f"{namedFlag}会计科目名称{str(random_num)}"
                        self.ADD_COA(base_url, cookie, this_Coacode, this_Coaname, coaaccountscheme)
                    num_this = 1
                    while True:
                        result = self.ADD_SOB(base_url, cookie, this_code, this_name, coaaccountscheme, exchangerate,
                                              currencyCode=currencyCode, useCurrencyCode=curr_code)
                        if result.get("msg") == "字段长度超过限制":
                            sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff',
                                                   auto_close_duration=5)
                            return "字段长度超出限制，请控制下长度[末尾特征标记]"
                        if result.get('msg') == 'success':
                            result3 = self.QUERY_SOB(base_url, cookie, sobCode=this_code)
                            sob_id = result3.get('page').get('list')[0].get('id')
                            coaSchemeId = result3.get('page').get('list')[0].get('coaSchemeId')
                            coaSchemeId_list.append(coaSchemeId)
                            sob_id_list.append(sob_id)
                            break
                        else:
                            if num_this >= 3:
                                msg_ = result.get('msg')
                                if msg_:
                                    msg += msg_ + '\n'
                                break
                            else:
                                num_this += 1
                                continue
                    if num_this >= 3:
                        break
            sob_list = [list(t) for t in zip(sob_id_list, coaSchemeId_list)]
            # 业务科目配置
            try:
                t8 = threading.Thread(target=self.ADD_BIZ2ACCOUNT, args=(base_url, cookie, sob_list, caregory_list,
                                                   accountType_list, num_makeing))
                t8.daemon = True
                t8.start()
            except Exception as error:
                msg += f"业务科目配置 执行异常：{error}\n"
                sg.popup(f"业务科目配置 执行异常：{error}")
            # self.ADD_BIZ2ACCOUNT(base_url, cookie, sob_list=sob_list, bizCategoryId_list=caregory_list,
            #                                        accountType_list=accountType_list, goal=num_makeing)

            # 税务科目配置
            try:
                t9 = threading.Thread(target=self.ADD_TAXRATEC, args=(base_url, cookie, sob_list, taxrate_list,
                                                   accountType_list, num_makeing))
                t9.daemon = True
                t9.start()
            except Exception as error:
                msg += f"税务科目配置 执行异常：{error}\n"
                sg.popup(f"税务科目配置 执行异常：{error}")
            # 结算科目配置
            try:
                payment_list = [list(t) for t in zip(paymentmode_list, transferFundFlag_list)]
                t10 = threading.Thread(target=self.ADD_PAYMODEACCT, args=(base_url, cookie, sob_list, payment_list,
                                                   num_makeing))
                t10.daemon = True
                t10.start()
            except Exception as error:
                traceback.print_exc()
                msg += f"结算科目配置 执行异常：{error}\n"
                sg.popup(f"结算科目配置 执行异常：{error}")
        except Exception as e:
            traceback.print_exc()
            return msg + '\n' + str(e)

    # 凭证配置(会计结构建立、辅助核算建立)
    def create_acctstructures(self, confList, value, **kwargs):
        """
                confList：配置文件对象，dict
                value：界面值
                kwargs: namedflag每笔数据的标记；
                """
        method_name = "凭证配置"
        msg = ''
        numdas = 1
        try:
            try:
                kwargs.update(confList)
                kwargs.update(value)
                base_url = kwargs.get('base_url')
                cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
                num_makeing = abs(int(kwargs.get('num_acctstructures')) if kwargs.get('num_acctstructures') else 0)  # 会计结构\辅助核算建立数
                if num_makeing <= 0:
                    return
                namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
            except:
                sg.popup_quick_message("字段值异常，造数数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                       background_color='#0099ff', auto_close_duration=5)
                return

            # 查账套id列表
            result23 = self.QUERY_SOB(base_url, cookie, validityFlag='0')

            sobId_list = self.GET_VALUE_FROM_JSON_DICT(result23, 'id')

            if not len(sobId_list):
                sobId_list = self.CREATE_SOBS(base_url, cookie, 2, namedFlag)
            for numas in range(1, num_makeing + 1):
                random_num = self.random_character(char_type="str", length="8")
                this_code = f"{namedFlag}kjjg_{str(random_num)}"
                this_name = f"{namedFlag}会计结构{str(random_num)}"
                result = self.ADD_ACCOUNTSTRUCTURES(base_url, cookie, structureCode=this_code, structureName=this_name, namedFlag=namedFlag)
                if result.get("msg") == "字段长度超过限制":
                    sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff',
                                           auto_close_duration=5)
                    return "字段长度超出限制，请控制下长度[末尾特征标记]"
                if result.get('msg') == 'success':
                    list_base_num_more[4] += 1
                    numdas += 1
                    secondarySegmentId = self.GET_DICT(result, 'id')  # 会计结构id
                    # 关联账套
                    sob_choiced = random.choice(sobId_list)
                    self.LINK_SOB_SECONDARYSEGMENT(base_url, cookie, sobId=sob_choiced, secondarySegment=secondarySegmentId)
                    # 新增辅助核算
                    this_code2 = f"{namedFlag}fzhs_{str(random_num)}"
                    this_name2 = f"{namedFlag}辅助核算{str(random_num)}"
                    result33 = self.ADD_STRUCTURESGROUP(base_url, cookie, sobId=sob_choiced, structureGroupName=this_name2, structureGroupCode=this_code2)
                    if result33.get('msg') == 'success':
                        list_base_num_more[5] += 1
                        result45 = self.QUERY_STRUCTURESGROUP(base_url, cookie, structureGroupCode=this_code2)
                        structureGroupId = self.GET_DICT(result45, 'id')
                        ids = self.GET_DICT(self.FINDDATA(base_url, cookie, f'page=1&limit=20&orderField=&order=&lovKey='
                              f'sysStructuresGroup.findStructureDetails&SOB_ID={sob_choiced}&hasCount=N'), 'ID')
                        self.ADD_STRUCTCONF(base_url, cookie, id=structureGroupId, ids=ids)
                    else:
                        msg_ = result.get('msg')
                        if msg_:
                            msg += msg_ + '\n'
                else:
                    msg_ = result.get('msg')
                    if msg_:
                        msg += msg_ + '\n'
        except Exception as e:
            traceback.print_exc()
            return msg + '\n' + str(e)

    # SAP相关配置(特殊总账类型、特殊总账标识)
    def create_specialgl(self, confList, value, **kwargs):
        """
                confList：配置文件对象，dict
                value：界面值
                kwargs: namedflag每笔数据的标记；
                """
        method_name = "特殊总账类型、特殊总账标识新增"
        msg = ''
        numdas_1, numdas_2 = 1, 1

        try:
            try:
                kwargs.update(confList)
                kwargs.update(value)
                base_url = kwargs.get('base_url')
                cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
                num_makeing = abs(int(kwargs.get('num_specialgl')) if kwargs.get('num_specialgl') else 0)  # 特殊总账类型、特殊总账标识新增数
                if num_makeing <= 0:
                    return
                namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
            except:
                sg.popup_quick_message("字段值异常，造数数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                       background_color='#0099ff', auto_close_duration=5)
                return

            # 查账套id列表
            result23 = self.QUERY_SOB(base_url, cookie, validityFlag='0', limit='20')
            len1212th = len(result23.get('page').get('list'))
            if len1212th < 20:
                self.CREATE_SOBS(base_url, cookie, 20-len1212th, namedFlag)
            result23 = self.QUERY_SOB(base_url, cookie, validityFlag='0', limit='20')
            sobId_list = self.GET_VALUE_FROM_JSON_DICT(result23, 'id')
            sobCode_list = self.GET_VALUE_FROM_JSON_DICT(result23, 'sobCode')
            sobName_list = self.GET_VALUE_FROM_JSON_DICT(result23, 'name')
            coaSchemeId_list = self.GET_VALUE_FROM_JSON_DICT(result23, 'coaSchemeId')

            sob_list = [list(k) for k in zip(sobId_list, sobCode_list, sobName_list, coaSchemeId_list)]
            accountType_list = ['A', 'D', 'K', 'S']

            for numas in range(1, num_makeing + 1):
                for accountType in accountType_list:
                    random_num = self.random_character(char_type="str", length="8")
                    this_code = f"{namedFlag}type_{str(random_num)}"
                    this_name = f"{namedFlag}总账类型{str(random_num)}"
                    sob = random.choice(sob_list)
                    result = self.ADD_SPECIALGLTYPE(base_url, cookie, sobId=sob[0], sobCode=sob[1],
                            sobName=sob[2], accountType=accountType, glCode=this_code, glName=this_name)
                    if result.get("msg") == "字段长度超过限制":
                        sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff',
                                               auto_close_duration=5)
                        return "字段长度超出限制，请控制下长度[末尾特征标记]"
                    if result.get('msg') == 'success':
                        list_base_num_more[6] += 1
                        numdas_1 += 1

                        # 科目
                        accountId_list = self.GET_VALUE_FROM_JSON_DICT(self.FINDDATA(base_url, cookie, f'page=1&limit=50&orderField=&order=&lovKey=sysCoaAccountErpMode.'
                            f'findNameAndCode&HIERARCHY=0&ACCOUNT_SCHEME_ID={sob[3]}&hasCount=N'), 'ID')
                        result33 = self.ADD_SAPSGL(base_url, cookie, sobCode=sob[1], coaAccountType=accountType, sgl=this_code,
                            controlAccountId=random.choice(accountId_list), targetAccountId=random.choice(accountId_list),
                                                   coaSchemeId=sob[3], sglName=this_name, sobName=sob[2])

                        if result33.get('msg') == 'success':
                            list_base_num_more[7] += 1
                            numdas_2 += 1
                            if numdas_1 > num_makeing and numdas_2 > num_makeing:
                                return "success"
                        else:
                            msg_ = result33.get('msg')
                            if msg_:
                                msg += msg_ + '\n'
                    else:
                        msg_ = result.get('msg')
                        if msg_:
                            msg += msg_ + '\n'
        except Exception as e:
            traceback.print_exc()
            msg += '\n' + str(e)
            return msg

    # SAP相关配置(记账代码)
    def create_postingkey(self, confList, value, **kwargs):
        """
                confList：配置文件对象，dict
                value：界面值
                kwargs: namedflag每笔数据的标记；
                """
        method_name = "记账代码"
        msg = ''
        numdas = 1
        try:
            try:
                kwargs.update(confList)
                kwargs.update(value)
                base_url = kwargs.get('base_url')
                cookie = self.GET_USER_COOKIE(base_url, kwargs.get('user_submit'), kwargs.get('passwd_submit'))
                num_makeing = abs(int(kwargs.get('num_postingkey')) if kwargs.get('num_postingkey') else 0)  # 记账代码
                if num_makeing <= 0:
                    return
                namedFlag = f"{kwargs.get('namedflag')}" if kwargs.get('namedflag') else ""  # 每笔数据的标记
            except:
                sg.popup_quick_message("字段值异常，造数数量需为正整数；\n配置文件中的'user_submit'、'passwd_submit'需能正常登录；",
                                       background_color='#0099ff', auto_close_duration=5)
                return

            # 查账套id列表
            result23 = self.QUERY_SOB(base_url, cookie, validityFlag='0', limit='20')
            len1212th = len(result23.get('page').get('list'))
            if len1212th < 20:
                self.CREATE_SOBS(base_url, cookie, 20-len1212th, namedFlag)
            result23 = self.QUERY_SOB(base_url, cookie, validityFlag='0', limit='20')
            sobId_list = self.GET_VALUE_FROM_JSON_DICT(result23, 'id')
            sobCode_list = self.GET_VALUE_FROM_JSON_DICT(result23, 'sobCode')
            sobName_list = self.GET_VALUE_FROM_JSON_DICT(result23, 'name')

            sob_list = [list(k) for k in zip(sobId_list, sobCode_list, sobName_list)]

            for numas in range(1, num_makeing + 1):
                random_num = self.random_character(char_type="str", length="6")
                this_code = f"{namedFlag}unavailable_{str(random_num)}"
                this_name = f"{namedFlag}不可用的记账代码{str(random_num)}"
                sob = random.choice(sob_list)
                result = self.ADD_POSTINGKEY(base_url, cookie, sobId=sob[0], sobCode=sob[1],
                        sobName=sob[2], postingKeyCode=this_code, postingKeyName=this_name)
                if result.get("msg") == "字段长度超过限制":
                    sg.popup_quick_message("字段长度超出限制，请控制下长度[末尾特征标记]", background_color='#0099ff',
                                           auto_close_duration=5)
                    return "字段长度超出限制，请控制下长度[末尾特征标记]"
                if result.get('msg') == 'success':
                    list_base_num[15] += 1
                    numdas += 1
                else:
                    msg_ = result.get('msg')
                    if msg_:
                        msg += msg_ + '\n'

        except Exception as e:
            traceback.print_exc()
            msg += '\n' + str(e)
            return msg

ZTCCLOUD = ZTCCLOUD()

baseData = baseData()
"""造公司部门岗位员工数据window"""
def windowCreateCompDept(**kwargs):
    """造公司部门数据window"""
    global test_env, path, base_url
    path = path_pic + rf'CHN_TGtools({test_env.split("//")[-1]}).ini'  # 配置文件路径

    a2 = read_conf()  # 读取配置文件数据

    # base_url = a2["base_url"]  # 基础url
    base_url = test_env or list_test_env[0]

    layout = tool_ui_set(a2)
    windows = sg.Window("创建公司部门", layout, finalize=True, enable_close_attempted_event=True)  # 加载布局生成窗口
    while True:
        event, value = windows.read(300)
        if event == "创建":
            try:
                # 批量提单
                t1 = threading.Thread(target=baseData.create_comp_dept, args=(a2, value, base_url))
                # 如果某个子线程的daemon属性为True，主线程运行结束时不对这个子线程进行检查而直接退出，
                # 同时所有daemon值为True的子线程将随主线程一起结束，而不论是否运行完成。
                t1.daemon = True
                t1.start()

            except Exception as error:
                sg.popup(f"执行异常：{error}")
                # raise error

        elif event in right_button_menu or event in ['bill_type', 'base_url', "配置"]:  # 右键菜单事件或切窗口
            event_base_config(event, value, windows, a2)

        elif event in [sg.WINDOW_CLOSE_ATTEMPTED_EVENT, sg.WIN_X_EVENT] and sg.popup_yes_no('退出程序?', title='确认') == 'Yes':
            windows.close()

        elif event in [sg.WINDOW_CLOSED, sg.WIN_CLOSED]:
            try:
                windows.close()
                break
            except:
                return


"""造提单数据window"""
def windowSubmitBoe():
    """造提单数据window"""

    global fial_boeNo, success_boeNo, list_boeNo, success_boe_pay, nodeName, base_url, test_env, list_test_env

    test_ip = test_env.split("//")[-1]
    path = path_pic + rf'CHN_TGtools({test_ip}).ini'  # 配置文件路径

    a2 = read_conf()  # 读取配置文件数据
    # base_url = a2["base_url"]  # 基础url
    base_url = test_env or list_test_env[0]
    try:
        list_user_submit = eval(a2.get('list_user_submit'))
    except:
        print(f'配置文件中list_user_submit必须为list格式')
        return
    layout = tool_ui(a2)  # 加载工具的布局

    winmain_name = f'FOL_SubmitBoe_THS ({test_ip})'
    global win_main
    win_main = sg.Window(
        winmain_name,
        layout,
        finalize=True,
        enable_close_attempted_event=True)  # 加载布局生成窗口
    while True:
        global event, value2
        event, value2 = win_main.read(300)
        if event == "执行":
            global Number_Submit
            Number_Submit = 0
            global MaxWorkers
            MaxWorkers = int(value2["MaxWorkers"])
            nodeName = value2["nodename"]
            split_line("执行开始")
            save_data(value2, a2, flag_UpdateConf)
            try:
                # 读取界面数据
                base_url = value2.get('base_url')
                global destination
                destination = str(value2.get('destination'))
                user_submit = value2.get('user_submit')  # 提单人
                passwd_submit = value2.get('passwd_submit')
                user_submit_cookie = login(user_submit, passwd_submit)  # 提单人cookie
                if not user_submit_cookie:
                    continue
                # 保存提单人账号
                if user_submit not in list_user_submit and isinstance(list_user_submit, list):
                    list_user_submit.append(user_submit)
                    write_conf(path, 'CTG', 'list_user_submit', value=list_user_submit)
                    win_main['user_submit'].update(value=user_submit, values=list_user_submit)

                boe_body = json_java_py(value2.get('boe_body'))
                goal_boe = re.findall(
                    r'"boeNo":[ ]?"(.+?)",',
                    boe_body)  # 从请求体捞出来的单号，替换此号码

                if not len(goal_boe):
                    sg.popup("[单据请求体]中不含单号\n请检查", title='提示')
                    continue
                else:
                    goal_boe = goal_boe[0]
                try:
                    if not isinstance(eval(boe_body), dict):
                        raise Exception("[单据请求体]可能不是json格式")
                except Exception as error:
                    # raise error
                    sg.popup("[单据请求体]可能不是json格式\n请检查", title='提示')
                    continue

                bill_type = value2['bill_type']
                if bill_type not in list_bill_type:
                    sg.popup("该[场景]暂不支持\n请重选", title='提示')
                    continue

                times = value2['times'] if value2['times'] else 0
                if input_check(times):
                    sg.popup("[次数]校验错误！\n请输入正整数", title='提示')
                    continue
                try:
                    times = int(times)
                except Exception as error:
                    sg.popup("[次数]校验错误！\n请输入正整数", title='提示')
                    print("[次数]请输入正整数.error:\n", error)
                    continue
                if times > 500:
                    sg.popup_quick_message("提单真多，挺好。\n 下次别这样了。", background_color='#0099ff')
                """多线程提单"""
                if destination == destination_list[0]:
                    if sg.popup_yes_no(
                            "确认只是制造提单成功数据？", title='选择') == "No":
                        split_line("放弃执行")
                        continue
                # 仅选b2
                elif destination == destination_list[1] and sg.popup_yes_no("确认只是制造到共享的单据？", title='选择') == "No":
                    split_line("放弃执行")
                    continue
                if times != 0:
                    print(
                        f"\n┄┅┄┅┄┅┄┅┄┅┄┅ 单据初始化、提单:{str(times)}张单据 ┅┄┅┄┅┄┅┄┅┄┅┄")
                    print(f"\n执行中，请等待。可到[我的单据]菜单查看实时情况...")

                # 批量提单
                t1 = threading.Thread(
                    target=submit_boe_ths, args=(
                        user_submit_cookie, boe_body, goal_boe, times))
                # 如果某个子线程的daemon属性为True，主线程运行结束时不对这个子线程进行检查而直接退出，
                # 同时所有daemon值为True的子线程将随主线程一起结束，而不论是否运行完成。
                t1.daemon = True
                t1.start()

                # 业务审批
                if destination in destination_list[1:]:
                    t2 = threading.Thread(
                        target=jump_ths, args=(
                            user_submit_cookie,))
                    t2.daemon = True
                    t2.start()

                # 共享审批
                if destination == destination_list[2]:
                    t3 = threading.Thread(
                        target=jump_to_complete_ths, args=(
                            user_submit_cookie,))
                    t3.daemon = True
                    t3.start()

            except Exception as e:
                traceback.print_exc()
                print(f"执行出错，错误信息：{e}")
                split_line("执行中止")
                # raise e
                continue

        elif event == "获取支付指令":
            user_fsc = value2['user_submit']  # 共享审批人
            passwd_fsc = value2['passwd_submit']
            user_fsc_cookie = login(user_fsc, passwd_fsc)  # 共享审批人cookie
            if user_fsc_cookie:
                pass
            else:
                continue
            if list_boeNo == []:
                sg.popup("请先生成单据列表", title='提示')
                continue

            split_line("获取开始")
            print("该流程可能出现响应阻塞，请耐心等待")
            list_boeNo_this = list_boeNo
            thread5 = threading.Thread(
                target=Get_pageEfsAndHisEfs_THS, args=(user_fsc_cookie, list_boeNo_this))
            thread5.daemon = True
            thread5.start()

        elif event == "暂存":
            save_data(value2, a2, flag_UpdateConf)
            path = path_pic + rf'CHN_TGtools({test_env.split("//")[-1]}).ini'  # 配置文件路径
            if value2.get('base_url'):
                choice_env(flag="write", content=value2.get('base_url'))
                try:
                    list_test_env = eval(a2.get('list_test_env'))
                except:
                    print(f'配置文件中list_test_env必须为list格式')
                    return
                if value2.get('base_url') not in list_test_env and isinstance(list_test_env, list):
                    list_test_env.append(value2.get('base_url'))
                    write_conf(path, 'CTG', 'list_test_env', value=list_test_env)
                    win_main['base_url'].update(values=list_test_env)
                    sg.popup_ok('全新的环境地址信息\n将重载数据')
                    cut_windows(value2, win_main)


        elif event == "查看":
            flag = True
            if list_boeNo != []:
                print(f"\n审批完成的单据共{len(success_boeNo)}个：\n{success_boeNo}\n")
                flag = False
            if q.qsize():
                list21 = []
                for i in list(q.queue):
                    list21.append(i.get('boeNo'))
                print(f"\n待业务审批的单据共{q.qsize()}个：\n{list21}\n")
                flag = False

            if boe_jump_ths_queue.qsize():
                print(f"\n待共享审批的单据共{boe_jump_ths_queue.qsize()}个：\n{list(boe_jump_ths_queue.queue)}\n")
                flag = False

            if flag:
                print(f"别看了，还没有成功生成的单据...\n")

        elif event in right_button_menu or event in ['bill_type', 'base_url', "配置"]:  # 右键菜单事件或切窗口
            event_base_config(event, value2, win_main, a2)

        elif event in [sg.WINDOW_CLOSE_ATTEMPTED_EVENT, sg.WIN_X_EVENT] and sg.popup_yes_no('退出程序?',
                                                                                                title='确认') == 'Yes':
            win_main.close()

        elif event in [sg.WINDOW_CLOSED, sg.WIN_CLOSED]:
            try:
                win_main.close()
                break
            except:
                return


"""接口自动化请求体格式化window"""
def windowFormatParams(**kwargs):
    """接口自动化请求体格式化window"""

    global test_env, path

    path = path_pic + rf'CHN_TGtools({test_env.split("//")[-1]}).ini'  # 配置文件路径

    a2 = read_conf()  # 读取配置文件数据

    layout = format_params_ui(a2)
    F = FORMAT_PAPAMS()
    windows = sg.Window("Params格式化工具", layout, finalize=True, enable_close_attempted_event=True)  # 加载布局生成窗口
    while True:
        event, value = windows.read(300)
        if event == "处理":
            windows['debug_result'].Update(value='')
            windows['must_para'].update(value="")
            content = value.get('params')
            pointkey = value.get('pointkey')
            try:
                t4 = threading.Thread(
                    target=F.format, args=(content, pointkey), kwargs={'windows': windows})
                # 如果某个子线程的daemon属性为True，主线程运行结束时不对这个子线程进行检查而直接退出，
                # 同时所有daemon值为True的子线程将随主线程一起结束，而不论是否运行完成。
                t4.daemon = True
                t4.start()
                # F.format(content, pointkey)
            except Exception as error:
                sg.popup(f"程序出现异常：{error}", title='Error')
        elif event == "重置":
            windows['debug_result'].Update(value='')
            windows['params'].Update(value='')

        elif event in right_button_menu or event in ['bill_type', 'base_url']:  # 右键菜单事件或切窗口
            event_base_config(event, value, windows, a2)

        elif event in [sg.WINDOW_CLOSE_ATTEMPTED_EVENT, sg.WIN_X_EVENT] and sg.popup_yes_no('退出程序?', title='确认') == 'Yes':
            windows.close()

        elif event in [sg.WINDOW_CLOSED, sg.WIN_CLOSED]:
            try:
                windows.close()
                break
            except:
                return


"""数据工厂"""
def windowDataTools(**kwargs):
    """数据工厂"""

    global base_url, test_env, test_env, path
    path = path_pic + rf'CHN_TGtools({test_env.split("//")[-1]}).ini'  # 配置文件路径

    a2 = read_conf()  # 读取配置文件数据

    base_url = test_env or list_test_env[0]

    layout = tool_data_ui(a2)

    windows = sg.Window("数据工厂", layout, finalize=True, enable_close_attempted_event=True)  # 加载布局生成窗口
    while True:
        event, value = windows.read(300)
        if event == "執行":
            base_url = value.get('base_url')
            print("执行开始，请点击[進度]查询执行进度。右键可清空所有进度记录。\n", end='')
            list = baseData.__dir__()
            list_tool = []
            for tool in list:
                if tool.startswith("create_"):
                    list_tool.append(tool)
            if len(list_base_key) != len(list_tool):
                print("代码配置列表长度不一致")
                continue

            for key, val in enumerate(list_base_key):
                if value.get(val) is True:
                    try:
                        t1 = threading.Thread(target=eval("baseData." + list_tool[key]), args=(a2, value))
                        t1.daemon = True
                        t1.start()
                    except Exception as error:
                        sg.popup(f"{list_base[key]} 执行异常：{error}")

        elif event == "進度":
            # 获取进度
            strget, strget2, result = "", "", ""
            for key, value in enumerate(list_base_num):
                if int(value) != 0:
                    name = list_base[key]
                    strget += f"{name}: {str(value)}\n"
            for key, value in enumerate(list_base_num_more):
                if int(value) != 0:
                    name = list_base_more[key]
                    strget2 += f"{name}: {str(value)}\n"
            if len(strget):
                result += strget
            if len(strget2):
                result += strget2
            if result:
                sg.popup_quick_message("\n当前进度：\n" + result, background_color="green", auto_close_duration=5)
            else:
                sg.popup_quick_message("暂无数据", auto_close_duration=3, background_color='#0099ff')

        elif event == "开始":
            sg.popup_quick_message("未完待续...", background_color="#0099ff", auto_close_duration=5)


        elif event == "allin":
            text = windows["allin"].get_text()
            for i in list_base_key:
                if text == "全√":
                    windows[i].Update(True)
                else:
                    windows[i].Update(False)

            windows['allin'].update("不√全√".replace(text, ""))

        elif event in right_button_menu or event in ['bill_type', 'base_url', "配置"]:  # 右键菜单事件或切窗口
            event_base_config(event, value, windows, a2)

        elif event in [sg.WINDOW_CLOSE_ATTEMPTED_EVENT, sg.WIN_X_EVENT] and sg.popup_yes_no('退出程序?',
                                                                                            title='确认') == 'Yes':
            windows.close()

        elif event in [sg.WINDOW_CLOSED, sg.WIN_CLOSED]:
            try:
                windows.close()
                break
            except:
                return




if __name__ == '__main__':
    config = configparser.RawConfigParser()
    path_pic = rf'{os.getenv("APPDATA")}' + '\\CHN_TGtools' + '\\'  # 应用文件路径
    env_path = path_pic + "test_env.txt"
    path = ""
    test_env = choice_env(flag="read")

    base_url = ""

    flag_UpdateConf = False  # 是否更新配置文件的标志

    """提单页"""
    windowSubmitBoe()

    # """接口自动化请求体格式化window"""
    # windowFormatParams()

    # """数据工厂页"""
    # windowDataTools()

